-
Notifications
You must be signed in to change notification settings - Fork 2
/
matio.visum.pas
156 lines (147 loc) · 4.66 KB
/
matio.visum.pas
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
unit matio.visum;
////////////////////////////////////////////////////////////////////////////////
//
// Author: Jaap Baak
// https://github.com/transportmodelling/matio
//
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
interface
////////////////////////////////////////////////////////////////////////////////
Uses
SysUtils, Classes, zlib, matio;
Type
TVisumMatrixReader = Class(TMatrixReader)
// Source code based on: https://github.com/MaxBo/matrixconverters/
private
AllNull: Boolean;
NColumns: Integer;
DataType: Int16;
Factor: Float32;
Compression: ANSIChar;
Reader: TBinaryReader;
protected
Procedure Read(const CurrentRow: Integer; const Rows: TCustomMatrixRows); override;
public
Constructor Create(const FileName: String);
Destructor Destroy; override;
end;
////////////////////////////////////////////////////////////////////////////////
implementation
////////////////////////////////////////////////////////////////////////////////
Constructor TVisumMatrixReader.Create(const FileName: String);
Var
Id: ANSIString;
begin
inherited Create(FileName);
SetCount(1); // Visum binary matrix file only contains a single matrix
// Read header
Reader := TBinaryReader.Create(FileStream);
// Read Id-length
var IdLength := Reader.ReadInt16;
if IdLength = 3 then
begin
// Read Id
Id := '';
for var Chr := 1 to IdLength do Id := Id + ANSIChar(Reader.ReadByte);
Compression := Id[3];
// Skip header
var HeaderLength := Reader.ReadInt16;
for var Chr := 1 to HeaderLength do Reader.ReadByte;
// Read parameters
Reader.ReadInt32;
Reader.ReadInt32;
Reader.ReadInt32;
Factor := Reader.ReadSingle;
SetSize(Reader.ReadInt32);
DataType := Reader.ReadInt16;
Reader.ReadBoolean;
// Skip row & column labels
if Compression = 'I' then
begin
NColumns := Size;
// Skip zone numbers
for var Zone := 1 to Size do Reader.ReadInt32;
end else
if Compression in ['K','L'] then
begin
NColumns := Reader.ReadInt32;
if NColumns <= Size then
begin
// Skip row numbers
for var Row := 1 to Size do Reader.ReadInt32;
// Skip column numbers
for var Column := 1 to NColumns do Reader.ReadInt32;
// Skip row labels
for var Row := 1 to Size do
begin
var n_chars := Reader.ReadInt32;
for var Ch := 1 to n_chars do Reader.ReadInt16; // utf16 characters
end;
// Skip column labels
for var Column := 1 to NColumns do
begin
var n_chars := Reader.ReadInt32;
for var Ch := 1 to n_chars do Reader.ReadInt16; // utf16 characters
end;
end else
raise Exception.Create('Too many columns in matrix')
end else
raise Exception.Create('Unknown compression method');
// Read allnull-flag
AllNull := Reader.ReadBoolean;
Reader.ReadDouble; // Diagonal sum
end else
raise Exception.Create('Invalid header');
end;
Procedure TVisumMatrixReader.Read(const CurrentRow: Integer; const Rows: TCustomMatrixRows);
Var
BytesStream: TBytesStream;
DecompressionStream: TZDecompressionStream;
DecompressionReader: TBinaryReader;
begin
if not AllNull then
begin
BytesStream := nil;
DecompressionStream := nil;
DecompressionReader := nil;
try
// Read compressed data
var len_chun := Reader.ReadInt32;
BytesStream := TBytesStream.Create;
BytesStream.Size := len_chun;
Reader.Read(BytesStream.Bytes,0,len_chun);
// Decompress data
BytesStream.Position := 0;
DecompressionStream := TZDecompressionStream.Create(BytesStream);
DecompressionReader := TBinaryReader.Create(DecompressionStream);
for var Column := 0 to Size-1 do
if Column < NColumns then
case DataType of
2: Rows[0,Column] := Factor*DecompressionReader.ReadInt16;
3: Rows[0,Column] := Factor*DecompressionReader.ReadInt32;
4: Rows[0,Column] := Factor*DecompressionReader.ReadSingle;
5: Rows[0,Column] := Factor*DecompressionReader.ReadDouble;
end
else
Rows[0,Column] := 0.0;
// Read row & column sums
if Compression in ['I','K'] then
begin
Reader.ReadDouble; // Row sums
Reader.ReadDouble; // column sums
end;
finally
DecompressionStream.Free;
DecompressionReader.Free;
BytesStream.Free;
end;
end else
for var Column := 0 to Size-1 do Rows[0,Column] := 0.0;
end;
Destructor TVisumMatrixReader.Destroy;
begin
Reader.Free;
inherited Destroy;
end;
end.