{******************************************} { } { FastReport VCL } { Map Shape } { } { Copyright (c) 1998-2021 } { by Fast Reports Inc. } { } {******************************************} unit frxMapShape; interface {$I frx.inc} uses {$IFDEF FPC} LCLType, LMessages, LazHelper, LCLIntf, LConvEncoding, LazUTF8, {$ELSE} Windows, {$ENDIF} Classes, Contnrs, frxClass, Graphics, Types, frxMapHelpers, frxOSMFileFormat, frxGPXFileFormat, frxERSIShapeFileFormat, frxAnaliticGeometry; type TShapeStyle = class(TPersistent) private FBorderColor: TColor; FBorderStyle: TPenStyle; FBorderWidth: Integer; FFillColor: TColor; FPointSize: Extended; public constructor Create; procedure TunePen(Pen: TPen); published property BorderColor: TColor read FBorderColor write FBorderColor; property BorderStyle: TPenStyle read FBorderStyle write FBorderStyle; property BorderWidth: Integer read FBorderWidth write FBorderWidth; property FillColor: TColor read FFillColor write FFillColor; property PointSize: Extended read FPointSize write FPointSize; // Diameter end; (******************************************************************************) TShape = class(TPersistent) private FZoom: Extended; FCenterOffsetX: Extended; FCenterOffsetY: Extended; FOffsetX: Extended; FOffsetY: Extended; FValue: Extended; function GetShapeType: TShapeType; function GetLegend(const Name: String): String; function GetPartCount: integer; function GetShapeCenter: TfrxPoint; function GetShapeTags: TStringList; protected FShapeData: TShapeData; procedure Clear; virtual; public procedure Write(Writer: TWriter); procedure WriteData(Writer: TWriter); procedure Read(Reader: TReader); procedure ReadData(Reader: TReader); constructor CreateClear; destructor Destroy; override; function IsValueEmpty: Boolean; function IsMercatorSuitable: Boolean; property Value: Extended read FValue write FValue; property ShapeType: TShapeType read GetShapeType; property Legend[const Name: String]: String read GetLegend; property PartCount: integer read GetPartCount; property ShapeCenter: TfrxPoint read GetShapeCenter; property ShapeData: TShapeData read FShapeData; property OffsetX: Extended read FOffsetX write FOffsetX; property OffsetY: Extended read FOffsetY write FOffsetY; property Zoom: Extended read FZoom write FZoom; published property CenterOffsetX: Extended read FCenterOffsetX write FCenterOffsetX; property CenterOffsetY: Extended read FCenterOffsetY write FCenterOffsetY; property ShapeTags: TStringList read GetShapeTags; end; TAdjustableShape = class(TShape) published property OffsetX; property OffsetY; property Zoom; end; (******************************************************************************) TShapeList = class(TObjectList) private FEmbeddedData: Boolean; FAdjustableShape: Boolean; function GetShape(Index: Integer): TShape; procedure SetShape(Index: Integer; const AShape: TShape); protected FXMin, FXMax, FYMin, FYMax: Extended; FConverter: TMapToCanvasCoordinateConverter; FValidMapRect: Boolean; procedure SetMapRect(XMin, XMax, YMin, YMax: Extended); function Data(iRecord, iPart, iPoint: Integer): TDoublePoint; public constructor Create(Converter: TMapToCanvasCoordinateConverter); function AddShapeData(const AShapeData: TShapeData): integer; procedure ReplaceShapeData(iRecord: Integer; const AShapeData: TShapeData); procedure ReadDFM(Stream: TStream); procedure WriteDFM(Stream: TStream); procedure Read(Reader: TReader); procedure Write(Writer: TWriter); function IsGetValues(var Values: TDoubleArray): boolean; procedure ClearValues; function IsCanvasPolyPoints(iRecord, iPart: Integer; MapAccuracy, PixelAccuracy: Extended; var PolyPoints: TPointArray): boolean; function CanvasPoint(iRecord: Integer): TfrxPoint; function CanvasRect(iRecord: Integer): TDoubleRect; function CanvasPoly(iRecord: Integer): TDoublePointArray; function CanvasMatrix(iRecord: Integer): TDoublePointMatrix; function CanvasWidestPartBounds(iRecord: Integer): TfrxRect; procedure GetColumnList(List: TStrings); function IsValidMapRect(out MapRect: TfrxRect): boolean; procedure SetMapRectByData; function IsHasLegend(FieldName: String; Legend: String; out iRecord: Integer): boolean; function CanvasDistance(iRecord: Integer; P: TfrxPoint): Extended; function IsInside(iRecord: Integer; P: TfrxPoint): Boolean; // Canvas Point procedure SaveToTextFile(FileName: String); function IsMercatorSuitable: Boolean; property Items[Index: Integer]: TShape read GetShape write SetShape; default; property EmbeddedData: Boolean read FEmbeddedData write FEmbeddedData; property AdjustableShape: Boolean read FAdjustableShape write FAdjustableShape; end; (******************************************************************************) TOSMFileList = class (TStringList) public constructor Create; destructor Destroy; override; procedure AddFile(FileName: String); procedure AddLink(Link: String; Stream: TStream); function FileByName(FileName: String): TOSMFile; end; TOSMShapeList = class (TShapeList) protected procedure LoadData(OSMFile: TOSMFile; LayerTags: TStrings); public constructor CreateFromFile(FileName: String; Converter: TMapToCanvasCoordinateConverter; FileList: TOSMFileList; LayerTags: TStrings); end; (******************************************************************************) TGPXShapeList = class (TShapeList) protected procedure LoadData(GPXFile: TGPXFile); public constructor CreateFromFile(FileName: String; Converter: TMapToCanvasCoordinateConverter); end; (******************************************************************************) TERSIShapeList = class (TShapeList) protected procedure LoadData(ERSIFile: TERSIShapeFile); public constructor CreateFromFile(FileName: String; Converter: TMapToCanvasCoordinateConverter); end; (******************************************************************************) implementation uses Math, SysUtils, frxUtils, frxMapShapeTags {Editor}, frxMapLayerTags; { TShape } procedure TShape.Clear; begin FZoom := 1; FCenterOffsetX := 0; FCenterOffsetY := 0; FOffsetX := 0; FOffsetY := 0; FValue := NaN; end; constructor TShape.CreateClear; begin inherited Create; Clear; end; destructor TShape.Destroy; begin FShapeData.Free; inherited; end; function TShape.GetLegend(const Name: String): String; begin Result := FShapeData.Legend[Name]; end; function TShape.GetPartCount: integer; begin Result := FShapeData.PartCount; end; function TShape.GetShapeCenter: TfrxPoint; begin Result := FShapeData.ShapeCenter; end; function TShape.GetShapeTags: TStringList; begin Result := FShapeData.Tags; end; function TShape.GetShapeType: TShapeType; begin Result := FShapeData.ShapeType; end; function TShape.IsMercatorSuitable: Boolean; begin Result := (FShapeData <> nil) and FShapeData.IsMercatorSuitable; end; function TShape.IsValueEmpty: Boolean; begin Result := IsNaN(Value); end; procedure TShape.Read(Reader: TReader); begin FZoom := Reader.ReadFloat; FCenterOffsetX := Reader.ReadFloat; FCenterOffsetY := Reader.ReadFloat; FOffsetX := Reader.ReadFloat; FOffsetY := Reader.ReadFloat; FValue := Reader.ReadFloat; if FShapeData = nil then FShapeData := TShapeData.Create; FShapeData.ReadTags(Reader); end; procedure TShape.ReadData(Reader: TReader); begin FShapeData.ReadData(Reader); end; procedure TShape.Write(Writer: TWriter); begin Writer.WriteFloat(FZoom); Writer.WriteFloat(FCenterOffsetX); Writer.WriteFloat(FCenterOffsetY); Writer.WriteFloat(FOffsetX); Writer.WriteFloat(FOffsetY); Writer.WriteFloat(FValue); FShapeData.WriteTags(Writer); end; procedure TShape.WriteData(Writer: TWriter); begin FShapeData.WriteData(Writer); end; { TShapeList } function TShapeList.AddShapeData(const AShapeData: TShapeData): integer; begin if AdjustableShape then Result := Add(TAdjustableShape.CreateClear) else Result := Add(TShape.CreateClear); AShapeData.CalcBounds; Items[Count - 1].FShapeData := AShapeData; end; function TShapeList.CanvasDistance(iRecord: Integer; P: TfrxPoint): Extended; begin case Items[iRecord].ShapeType of stPoint: Result := Distance(CanvasPoint(iRecord), P); stPolyLine: Result := DistancePolyline(CanvasPoly(iRecord), P); stPolygon: Result := DistancePolygon(CanvasPoly(iRecord), P); stRect: Result := DistanceRect(CanvasRect(iRecord), P); stDiamond: Result := DistanceDiamond(CanvasRect(iRecord), P); stEllipse: Result := DistanceEllipse(CanvasRect(iRecord), P); stPicture, stLegend: Result := DistancePicture(CanvasRect(iRecord), P); stTemplate: Result := DistanceTemplate(CanvasPoly(iRecord), P); stMultiPoint: Result := DistanceMultiPoint(CanvasMatrix(iRecord), P); stMultiPolyLine: Result := DistanceMultiPolyline(CanvasMatrix(iRecord), P); stMultiPolygon: Result := DistanceMultiPolygon(CanvasMatrix(iRecord), P); else raise Exception.Create('Unknown ShapeType'); end; end; function TShapeList.CanvasMatrix(iRecord: Integer): TDoublePointMatrix; var iPart, iPoint : Integer; begin SetLength(Result, Items[iRecord].FShapeData.PartCount); for iPart := 0 to High(Result) do begin SetLength(Result[iPart], Items[iRecord].FShapeData.MultiLineCount[iPart]); for iPoint := 0 to High(Result[iPart]) do Result[iPart, iPoint] := DoublePoint(FConverter.Transform(Data(iRecord, iPart, iPoint))); end; end; function TShapeList.CanvasPoint(iRecord: Integer): TfrxPoint; begin Result := FConverter.Transform(Items[iRecord].FShapeData.Point); end; function TShapeList.CanvasPoly(iRecord: Integer): TDoublePointArray; var i: Integer; begin SetLength(Result, Items[iRecord].FShapeData.MultiLineCount[0]); for i := 0 to High(Result) do Result[i] := DoublePoint(FConverter.Transform(Data(iRecord, 0, i))); end; function TShapeList.CanvasRect(iRecord: Integer): TDoubleRect; begin Result := Items[iRecord].FShapeData.Rect; Result.TopLeft := DoublePoint(FConverter.Transform(Result.TopLeft)); Result.BottomRight := DoublePoint(FConverter.Transform(Result.BottomRight)); end; function TShapeList.CanvasWidestPartBounds(iRecord: Integer): TfrxRect; begin Result := FConverter.TransformRect(Items[iRecord].FShapeData.WidestPartBounds); end; procedure TShapeList.ClearValues; var i: integer; begin for i := 0 to Count - 1 do Items[i].Value := NaN; end; constructor TShapeList.Create(Converter: TMapToCanvasCoordinateConverter); begin FConverter := Converter; OwnsObjects := True; FValidMapRect := False; FAdjustableShape := False; inherited Create; end; function TShapeList.Data(iRecord, iPart, iPoint: Integer): TDoublePoint; begin Result := Items[iRecord].FShapeData.MultiLine[iPart, iPoint]; end; procedure TShapeList.GetColumnList(List: TStrings); var iRecord: Integer; begin for iRecord := 0 to Count - 1 do Items[iRecord].FShapeData.IsGetColumnList(List); end; function TShapeList.GetShape(Index: Integer): TShape; begin Result := (inherited Items[Index]) as TShape; end; function TShapeList.IsCanvasPolyPoints(iRecord, iPart: Integer; MapAccuracy, PixelAccuracy: Extended; var PolyPoints: TPointArray): boolean; var iPoint, UsedCount: Integer; Points: TDoublePointArray; begin Result := False; Items[iRecord].FShapeData.GetPolyPoints(Points, iPart); // Get Map points UsedCount := Length(Points); Simplify(Points, MapAccuracy, UsedCount); if UsedCount < 2 then Exit; for iPoint := 0 to UsedCount - 1 do // Get Canvas points with FConverter.Transform(Points[iPoint]) do Points[iPoint] := DoublePoint(X, Y); Simplify(Points, PixelAccuracy, UsedCount); if UsedCount < 2 then Exit; SetLength(PolyPoints, UsedCount); for iPoint := 0 to UsedCount - 1 do with Points[iPoint] do PolyPoints[iPoint] := Point(Round(X), Round(Y)); Finalize(Points); Result := True; end; function TShapeList.IsGetValues(var Values: TDoubleArray): boolean; var i, ValuesCount: Integer; begin SetLength(Values, Count); ValuesCount := 0; for i := 0 to Count - 1 do if not Items[i].IsValueEmpty then begin Values[ValuesCount] := Items[i].Value; ValuesCount := ValuesCount + 1; end; SetLength(Values, ValuesCount); Result := ValuesCount <> 0; end; function TShapeList.IsHasLegend(FieldName, Legend: String; out iRecord: Integer): boolean; begin iRecord := 0; while iRecord <= Count - 1 do if Legend = Items[iRecord].FShapeData.Legend[FieldName] then Break else iRecord := iRecord + 1; Result := iRecord <= Count - 1; end; function TShapeList.IsInside(iRecord: Integer; P: TfrxPoint): Boolean; begin case Items[iRecord].ShapeType of stPoint: Result := False; stPolyLine: Result := IsInsidePolyline(CanvasPoly(iRecord), P); stPolygon: Result := IsInsidePolygon(CanvasPoly(iRecord), P); stRect, stPicture, stLegend: Result := IsInsideRect(CanvasRect(iRecord), P); stDiamond: Result := IsInsideDiamond(CanvasRect(iRecord), P); stEllipse: Result := IsInsideEllipse(CanvasRect(iRecord), P); stTemplate: Result := IsInsidePolygon(CanvasPoly(iRecord), P); stMultiPoint: Result := False; stMultiPolyLine: Result := IsInsideMultiPolyline(CanvasMatrix(iRecord), P); stMultiPolygon: Result := IsInsideMultiPolygon(CanvasMatrix(iRecord), P); else raise Exception.Create('Unknown ShapeType'); end; end; function TShapeList.IsMercatorSuitable: Boolean; var i: Integer; begin Result := False; for i := 0 to Count - 1 do if not Items[i].IsMercatorSuitable then Exit; Result := True; end; function TShapeList.IsValidMapRect(out MapRect: TfrxRect): boolean; begin Result := FValidMapRect; if Result then MapRect := frxRect(FXMin, FYMin, FXMax, FYMax); end; procedure TShapeList.Read(Reader: TReader); var SavedCount, i: Integer; Shape: TShape; begin FValidMapRect := Reader.ReadBoolean; FXMin := Reader.ReadFloat; FXMax := Reader.ReadFloat; FYMin := Reader.ReadFloat; FYMax := Reader.ReadFloat; EmbeddedData := Reader.ReadBoolean; SavedCount := Reader.ReadInteger; if EmbeddedData then begin Clear; for i := 0 to SavedCount - 1 do begin if AdjustableShape then Shape := TAdjustableShape.CreateClear else Shape := TShape.CreateClear; Shape.Read(Reader); Shape.ReadData(Reader); Add(Shape); end; end else for i := 0 to Count - 1 do Items[i].Read(Reader); end; procedure TShapeList.ReadDFM(Stream: TStream); var Reader: TReader; begin Reader := TReader.Create(Stream, 4096); Read(Reader); Reader.Free; end; procedure TShapeList.ReplaceShapeData(iRecord: Integer; const AShapeData: TShapeData); begin AShapeData.CalcBounds; Items[iRecord].FShapeData.Free; Items[iRecord].FShapeData := AShapeData; end; procedure TShapeList.SaveToTextFile(FileName: String); function ShapeTypeName(ShapeType: TShapeType): String; begin case ShapeType of stNone: Result := 'Unknown'; stPoint: Result := 'Point'; stPolyLine: Result := 'PolyLine'; stPolygon: Result := 'Polygon'; stRect: Result := 'Rect'; stDiamond: Result := 'Diamond'; stEllipse: Result := 'Ellipse'; stPicture: Result := 'Picture'; stLegend: Result := 'Legend'; stTemplate: Result := 'Template'; stMultiPoint: Result := 'MultiPoint'; stMultiPolyLine: Result := 'MultiPolyLine'; stMultiPolygon: Result := 'MultiPolygon'; end; end; var F: TextFile; iShape, iPart, iPoint: Integer; begin AssignFile(F, FileName); if FileExists(FileName) then begin Append(F) end else Rewrite(F); WriteLn(F, 'Count: ' + IntToStr(Count)); for iShape := 0 to Count - 1 do with Items[iShape] do begin WriteLn(F, ' ' + ShapeTypeName(ShapeData.ShapeType) + ' PartCount: ' + IntToStr(ShapeData.PartCount)); for iPart := 0 to ShapeData.PartCount - 1 do begin System.Write(F, ' MultiLineCount: ' + IntToStr(ShapeData.MultiLineCount[iPart]) + ' '); for iPoint := 0 to ShapeData.MultiLineCount[iPart] - 1 do with ShapeData.MultiLine[iPart, iPoint] do System.Write(F, FloatToStr(X) + ' ' + FloatToStr(Y) + ','); WriteLn(F); end; end; CloseFile(F); end; procedure TShapeList.SetMapRect(XMin, XMax, YMin, YMax: Extended); begin FXMin := XMin; FXMax := XMax; FYMin := YMin; FYMax := YMax; FValidMapRect := True; end; procedure TShapeList.SetMapRectByData; const Margin = 0.05; var TotalBounds: TfrxRect; iRecord, iPart, iPoint: Integer; dWidth, dHeight: Extended; begin if Count > 0 then begin with Items[0].FShapeData.Point do TotalBounds := frxRect(X, Y, X, Y); for iRecord := 0 to Count - 1 do case Items[iRecord].FShapeData.ShapeType of stPoint: ExpandRect(TotalBounds, Items[iRecord].FShapeData.Point); else for iPart := 0 to Items[iRecord].FShapeData.PartCount - 1 do for iPoint := 0 to Items[iRecord].FShapeData.MultiLineCount[iPart] - 1 do ExpandRect(TotalBounds, Data(iRecord, iPart, iPoint)); end; dWidth := RectWidth(TotalBounds) * Margin; dHeight := RectHeight(TotalBounds) * Margin; with TotalBounds do SetMapRect(Left - dWidth, Right + dWidth, Top - dHeight, Bottom + dHeight); end else FValidMapRect := False; end; procedure TShapeList.SetShape(Index: Integer; const AShape: TShape); begin inherited Items[Index] := AShape; end; procedure TShapeList.Write(Writer: TWriter); var i: Integer; begin Writer.WriteBoolean(FValidMapRect); Writer.WriteFloat(FXMin); Writer.WriteFloat(FXMax); Writer.WriteFloat(FYMin); Writer.WriteFloat(FYMax); Writer.WriteBoolean(EmbeddedData); Writer.WriteInteger(Count); for i := 0 to Count - 1 do begin Items[i].Write(Writer); if EmbeddedData then Items[i].WriteData(Writer); end; end; procedure TShapeList.WriteDFM(Stream: TStream); var Writer: TWriter; begin Writer := TWriter.Create(Stream, 4096); Write(Writer); Writer.Free; end; { TShapeStyle } constructor TShapeStyle.Create; begin FBorderColor := clBlack; FBorderStyle := psSolid; FBorderWidth := 1; FFillColor := clWhite; FPointSize := 10; end; procedure TShapeStyle.TunePen(Pen: TPen); begin Pen.Color := BorderColor; Pen.Width := BorderWidth; Pen.Style := BorderStyle; end; { TOSMShapeList } constructor TOSMShapeList.CreateFromFile(FileName: String; Converter: TMapToCanvasCoordinateConverter; FileList: TOSMFileList; LayerTags: TStrings); var OSMFile: TOSMFile; begin inherited Create(Converter); FAdjustableShape := True; OSMFile := FileList.FileByName(FileName); LoadData(OSMFile, LayerTags); if OSMFile.IsValidBounds then SetMapRect(OSMFile.Xmin, OSMFile.Xmax, OSMFile.Ymin, OSMFile.Ymax) else SetMapRectByData; end; procedure TOSMShapeList.LoadData(OSMFile: TOSMFile; LayerTags: TStrings); procedure LoadPoints(OSMFile: TOSMFile); var iNode: integer; begin for iNode := 0 to OSMFile.CountOfNodes - 1 do with OSMFile.Nodes[iNode] do if (Tags.Count > 0) and IsHaveAnyTag(LayerTags) then AddShapeData(TShapeData.CreatePoint(Tags, Longitude, Latitude)); end; procedure LoadPolys(OSMFile: TOSMFile); var iWay, iNode, iCount: integer; FShapeData: TShapeData; DP: TDoublePoint; begin for iWay := 0 to OSMFile.CountOfWays - 1 do with OSMFile.Ways[iWay] do if IsHaveAnyTag(LayerTags) then begin FShapeData := TShapeData.CreatePoly(ShapeType, Tags, Count); iCount := 0; for iNode := 0 to Count - 1 do if OSMFile.IsGetNodeAsPoint(iWay, iNode, DP) then begin FShapeData.MultiLine[0, iCount] := DP; Inc(iCount); end; FShapeData.MultiLineCount[0] := iCount; AddShapeData(FShapeData); end; end; begin LoadPolys(OSMFile); LoadPoints(OSMFile); end; { TGPXShapeList } constructor TGPXShapeList.CreateFromFile(FileName: String; Converter: TMapToCanvasCoordinateConverter); var GPXFile: TGPXFile; begin inherited Create(Converter); FAdjustableShape := True; GPXFile := TGPXFile.Create(FileName); LoadData(GPXFile); if GPXFile.IsValidBounds then SetMapRect(GPXFile.Xmin, GPXFile.Xmax, GPXFile.Ymin, GPXFile.Ymax) else SetMapRectByData; GPXFile.Free; end; procedure TGPXShapeList.LoadData(GPXFile: TGPXFile); procedure LoadPoints(GPXFile: TGPXFile); var iWayPoint: integer; begin for iWayPoint := 0 to GPXFile.CountOfWayPoints - 1 do with GPXFile.WayPoints[iWayPoint] do AddShapeData(TShapeData.CreatePoint(Tags, Longitude, Latitude)); end; procedure LoadPolys(GPXFile: TGPXFile); var iTrack, iTrackSegment, iPoint: integer; FShapeData: TShapeData; DPA: TDoublePointArray; begin for iTrack := 0 to GPXFile.CountOfTracks - 1 do with GPXFile.Tracks[iTrack] do begin FShapeData := TShapeData.CreateFull(Count, ShapeType, Tags); for iTrackSegment := 0 to Count - 1 do begin GetSegmentPoints(iTrackSegment, DPA); FShapeData.MultiLineCount[iTrackSegment] := Length(DPA); for iPoint := 0 to High(DPA) do FShapeData.MultiLine[iTrackSegment, iPoint] := DPA[iPoint]; end; AddShapeData(FShapeData); end; end; begin LoadPolys(GPXFile); LoadPoints(GPXFile); end; { TERSIShapeList } constructor TERSIShapeList.CreateFromFile(FileName: String; Converter: TMapToCanvasCoordinateConverter); var ERSIShapeFile: TERSIShapeFile; begin inherited Create(Converter); FAdjustableShape := True; ERSIShapeFile := TERSIShapeFile.Create(FileName); LoadData(ERSIShapeFile); SetMapRect(ERSIShapeFile.Xmin, ERSIShapeFile.Xmax, ERSIShapeFile.Ymin, ERSIShapeFile.Ymax); ERSIShapeFile.Free; end; procedure TERSIShapeList.LoadData(ERSIFile: TERSIShapeFile); procedure AddPoint(Tags: TStringList; X, Y: Double); begin AddShapeData(TShapeData.CreatePoint(Tags, X, Y)); end; procedure AddMultiPoint(iRecord: Integer; Tags: TStringList); var iPoint: Integer; FShapeData: TShapeData; begin FShapeData := TShapeData.CreateFull(1, stMultiPoint, Tags); FShapeData.MultiPointCount := ERSIFile.MultiPointCount[iRecord]; for iPoint := 0 to High(FShapeData.MultiPointCount) do FShapeData.MultiPoint[iPoint] := ERSIFile.MultiPoint[iRecord, iPoint]; AddShapeData(FShapeData); end; procedure AddPoly(iRecord: Integer; ST: TShapeType; Tags: TStringList); var Count, iPart, iPoint: Integer; DPA: TDoublePointArray; FShapeData: TShapeData; begin Count := ERSIFile.PolyPartsCount[iRecord]; FShapeData := TShapeData.CreateFull(Count, ST, Tags); for iPart := 0 to Count - 1 do begin ERSIFile.GetPartPoints(DPA, iRecord, iPart); FShapeData.MultiLineCount[iPart] := Length(DPA); for iPoint := 0 to High(DPA) do FShapeData.MultiLine[iPart, iPoint] := DPA[iPoint]; end; AddShapeData(FShapeData); end; var iRecord, iColumn: Integer; Tags, Columns: TStringList; begin Columns := TStringList.Create; ERSIFile.GetColumnList(Columns); for iRecord := 0 to ERSIFile.RecordCount - 1 do begin Tags := TStringList.Create; for iColumn := 0 to Columns.Count - 1 do Tags.Add(Columns[iColumn] + Tags.NameValueSeparator + ERSIFile.LegendToString[iColumn, iRecord]); case ERSIFile.ERSIShapeType[iRecord] of ERSI_Point: with ERSIFile.Point[iRecord] do AddPoint(Tags, X, Y); ERSI_PolyLine: if Count > 1 then AddPoly(iRecord, stMultiPolyLine, Tags) else AddPoly(iRecord, stPolyLine, Tags); ERSI_Polygon: if Count > 1 then AddPoly(iRecord, stMultiPolygon, Tags) else AddPoly(iRecord, stPolygon, Tags); ERSI_MultiPoint: AddMultiPoint(iRecord, Tags); end; Tags.Free; end; Columns.Free; end; { TOSMFileList } procedure TOSMFileList.AddFile(FileName: String); var i: Integer; begin if not Find(SafeFileName(FileName), i) then AddObject(SafeFileName(FileName), TOSMFile.Create(FileName)) end; procedure TOSMFileList.AddLink(Link: String; Stream: TStream); var i: Integer; begin if not Find(SafeFileName(Link), i) then AddObject(SafeFileName(Link), TOSMFile.Create(Link, Stream)); end; constructor TOSMFileList.Create; begin inherited; Sorted := True; Duplicates := dupError; end; destructor TOSMFileList.Destroy; var i: integer; begin for i := 0 to Count - 1 do Objects[i].Free; inherited; end; function TOSMFileList.FileByName(FileName: String): TOSMFile; var Index: integer; begin if Find(SafeFileName(FileName), Index) then Result := Objects[Index] as TOSMFile else Result := nil; end; end.