From ccb49c53867ce330f41797787573f33a2090e261 Mon Sep 17 00:00:00 2001 From: Felix Obermaier Date: Fri, 6 Aug 2021 10:20:08 +0200 Subject: [PATCH 1/7] Improve DbaseReader fixes #226 --- SharpMap/Data/Providers/DbaseReader.cs | 315 +++++-------------------- 1 file changed, 62 insertions(+), 253 deletions(-) diff --git a/SharpMap/Data/Providers/DbaseReader.cs b/SharpMap/Data/Providers/DbaseReader.cs index 5aa4a0b9..b9d6b250 100644 --- a/SharpMap/Data/Providers/DbaseReader.cs +++ b/SharpMap/Data/Providers/DbaseReader.cs @@ -298,7 +298,7 @@ public uint CurrentRecordOid private void ParseDbfHeader(string filename) { - var buffer32 = new byte[32]; + byte[] buffer32 = new byte[32]; _dbfStream.Seek(0, SeekOrigin.Begin); _dbfStream.Read(buffer32, 0, 32); @@ -324,70 +324,43 @@ private void ParseDbfHeader(string filename) // ( headerlength // - 32 bytes for the base header // - 1 byte for the header terminator ) / 32 bytes for each field - var numberOfColumns = (_headerLength - 32 - 1) / 32; + int numberOfColumns = (_headerLength - 32 - 1) / 32; _dbaseColumns = new DbaseField[numberOfColumns]; - for (var i = 0; i < numberOfColumns; i++) + for (int i = 0; i < numberOfColumns; i++) { _dbfStream.Read(buffer32, 0, 32); using (var br = new BinaryReader(new MemoryStream(buffer32))) { _dbaseColumns[i] = new DbaseField(); - _dbaseColumns[i].ColumnName = Encoding.UTF7.GetString((br.ReadBytes(11))).Replace("\0", "").Trim(); - var fieldtype = br.ReadChar(); - switch (fieldtype) + _dbaseColumns[i].ColumnName = _fileEncoding.GetString(br.ReadBytes(11)).Replace("\0", "").Trim(); + char fieldType = br.ReadChar(); + _dbaseColumns[i].DataTypeCode = fieldType switch { - case 'L': - //_dbaseColumns[i].DataType = typeof (bool); - _dbaseColumns[i].DataTypeCode = TypeCode.Boolean; - break; - case 'C': - //_dbaseColumns[i].DataType = typeof (string); - _dbaseColumns[i].DataTypeCode = TypeCode.String; - break; - case 'D': - //_dbaseColumns[i].DataType = typeof (DateTime); - _dbaseColumns[i].DataTypeCode = TypeCode.DateTime; - break; - case 'N': - //_dbaseColumns[i].DataType = typeof (double); - _dbaseColumns[i].DataTypeCode = TypeCode.Double; - break; - case 'F': - //_dbaseColumns[i].DataType = typeof (float); - _dbaseColumns[i].DataTypeCode = TypeCode.Single; - break; - case 'B': - //_dbaseColumns[i].DataType = typeof (byte[]); - _dbaseColumns[i].DataTypeCode = TypeCode.Object; //Hack for - break; - default: - throw (new NotSupportedException("Invalid or unknown DBase field type '" + fieldtype + - "' in column '" + _dbaseColumns[i].ColumnName + "'")); - } + 'L' => TypeCode.Boolean, + 'C' => TypeCode.String, + 'D' => TypeCode.DateTime, + 'N' => TypeCode.Double, + 'F' => TypeCode.Single, + 'B' => TypeCode.Object //Hack for + , + _ => throw new NotSupportedException("Invalid or unknown DBase field type '" + fieldType + + "' in column '" + _dbaseColumns[i].ColumnName + "'") + }; _dbaseColumns[i].Address = br.ReadInt32(); if (i == 0) //Set it to 0 for first column and calculate it for the rest _dbaseColumns[i].Address = 0; else _dbaseColumns[i].Address = _dbaseColumns[i - 1].Address + _dbaseColumns[i - 1].Length; - var length = (int)br.ReadByte(); - if (length < 0) length = length + 256; + int length = br.ReadByte(); _dbaseColumns[i].Length = length; _dbaseColumns[i].Decimals = br.ReadByte(); - //If the double-type doesn't have any decimals, make the type an integer - //if (_dbaseColumns[i].Decimals == 0 && _dbaseColumns[i].DataType == typeof (double)) - // if (_dbaseColumns[i].Length <= 2) - // _dbaseColumns[i].DataType = typeof (Int16); - // else if (_dbaseColumns[i].Length <= 4) - // _dbaseColumns[i].DataType = typeof (Int32); - // else - // _dbaseColumns[i].DataType = typeof (Int64); if (_dbaseColumns[i].Decimals == 0 && _dbaseColumns[i].DataTypeCode == TypeCode.Double) { if (_dbaseColumns[i].Length < 3) //Range [-9, 99] - _dbaseColumns[i].DataTypeCode = TypeCode.Byte; + _dbaseColumns[i].DataTypeCode = TypeCode.SByte; else if (_dbaseColumns[i].Length < 5) //Range [-999, 9999] _dbaseColumns[i].DataTypeCode = TypeCode.Int16; else if (_dbaseColumns[i].Length < 10) @@ -400,106 +373,19 @@ private void ParseDbfHeader(string filename) } } - //using (var s = GetStream()) - //using(var br = new BinaryReader(s)) - //{ - // if (br.ReadByte() != 0x03) - // throw new NotSupportedException("Unsupported DBF Type"); - - // _lastUpdate = new DateTime(br.ReadByte() + 1900, br.ReadByte(), br.ReadByte()); - // //Read the last update date - // _numberOfRecords = br.ReadInt32(); // read number of records. - // _headerLength = br.ReadInt16(); // read length of header structure. - // _recordLength = br.ReadInt16(); // read length of a record - // s.Seek(29, SeekOrigin.Begin); //Seek to encoding flag - // _fileEncoding = GetDbaseLanguageDriver(br.ReadByte(), filename); //Read and parse Language driver - // s.Seek(32, SeekOrigin.Begin); //Move past the reserved bytes - - // var numberOfColumns = (_headerLength - 31)/32; // calculate the number of DataColumns in the header - // _dbaseColumns = new DbaseField[numberOfColumns]; - // for (var i = 0; i < _dbaseColumns.Length; i++) - // { - // _dbaseColumns[i] = new DbaseField(); - // _dbaseColumns[i].ColumnName = Encoding.UTF7.GetString((br.ReadBytes(11))).Replace("\0", "").Trim(); - // var fieldtype = br.ReadChar(); - // switch (fieldtype) - // { - // case 'L': - // //_dbaseColumns[i].DataType = typeof (bool); - // _dbaseColumns[i].DataTypeCode = TypeCode.Boolean; - // break; - // case 'C': - // //_dbaseColumns[i].DataType = typeof (string); - // _dbaseColumns[i].DataTypeCode = TypeCode.String; - // break; - // case 'D': - // //_dbaseColumns[i].DataType = typeof (DateTime); - // _dbaseColumns[i].DataTypeCode = TypeCode.DateTime; - // break; - // case 'N': - // //_dbaseColumns[i].DataType = typeof (double); - // _dbaseColumns[i].DataTypeCode = TypeCode.Double; - // break; - // case 'F': - // //_dbaseColumns[i].DataType = typeof (float); - // _dbaseColumns[i].DataTypeCode = TypeCode.Single; - // break; - // case 'B': - // //_dbaseColumns[i].DataType = typeof (byte[]); - // _dbaseColumns[i].DataTypeCode = TypeCode.Object; //Hack for - // break; - // default: - // throw (new NotSupportedException("Invalid or unknown DBase field type '" + fieldtype + - // "' in column '" + _dbaseColumns[i].ColumnName + "'")); - // } - // _dbaseColumns[i].Address = br.ReadInt32(); - // if (i > 0) _dbaseColumns[i].Address = _dbaseColumns[i - 1].Address + _dbaseColumns[i - 1].Length; - - // var length = (int) br.ReadByte(); - // if (length < 0) length = length + 256; - // _dbaseColumns[i].Length = length; - // _dbaseColumns[i].Decimals = br.ReadByte(); - - - // //If the double-type doesn't have any decimals, make the type an integer - // //if (_dbaseColumns[i].Decimals == 0 && _dbaseColumns[i].DataType == typeof (double)) - // // if (_dbaseColumns[i].Length <= 2) - // // _dbaseColumns[i].DataType = typeof (Int16); - // // else if (_dbaseColumns[i].Length <= 4) - // // _dbaseColumns[i].DataType = typeof (Int32); - // // else - // // _dbaseColumns[i].DataType = typeof (Int64); - // if (_dbaseColumns[i].Decimals == 0 && _dbaseColumns[i].DataTypeCode == TypeCode.Double) - // { - // if (_dbaseColumns[i].Length < 3) //Range [-9, 99] - // _dbaseColumns[i].DataTypeCode = TypeCode.Byte; - // else if (_dbaseColumns[i].Length < 5) //Range [-999, 9999] - // _dbaseColumns[i].DataTypeCode = TypeCode.Int16; - // else if (_dbaseColumns[i].Length < 10) - // _dbaseColumns[i].DataTypeCode = TypeCode.Int32; - // else if (_dbaseColumns[i].Length < 19) - // _dbaseColumns[i].DataTypeCode = TypeCode.Int64; - // else - // _dbaseColumns[i].DataTypeCode = TypeCode.double; - // } - - // br.BaseStream.Seek(s.Position + 14, 0); - // } - //} - _currentRecordBuffer = new byte[_recordLength]; _headerIsParsed = true; CreateBaseTable(); } - private static Encoding GetDbaseLanguageDriver(byte dbasecode) + private static Encoding GetDbaseLanguageDriver(byte dbaseCode) { - return GetDbaseLanguageDriver(dbasecode, null); + return GetDbaseLanguageDriver(dbaseCode, null); } - private static Encoding GetDbaseLanguageDriver(byte dbasecode, string fileName) + private static Encoding GetDbaseLanguageDriver(byte dbaseCode, string fileName) { - switch (dbasecode) + switch (dbaseCode) { case 0x01: return Encoding.GetEncoding(437); //DOS USA code page 437 @@ -732,8 +618,8 @@ private static Type TypeByTypeCode(TypeCode dataTypeCode) { switch (dataTypeCode) { - case TypeCode.Byte: - return typeof (byte); + case TypeCode.SByte: + return typeof (sbyte); case TypeCode.Boolean: return typeof (bool); case TypeCode.Int16: @@ -766,7 +652,7 @@ private void CreateBaseTable() _baseTable = new FeatureDataTable(); if (IncludeOid) { - _baseTable.Columns.Add("Oid", typeof(UInt32)); + _baseTable.Columns.Add("Oid", typeof(uint)); _baseTable.PrimaryKey = new DataColumn[] { _baseTable.Columns[0] }; } @@ -774,13 +660,13 @@ private void CreateBaseTable() { //_baseTable.Columns.Add(dbf.ColumnName, dbf.DataType); int suffix = 0; - string colname = suffix > 0 ? string.Format("{0}_{1}", dbf.ColumnName, suffix) : dbf.ColumnName; - while (_baseTable.Columns.Contains(colname)) + string colName = dbf.ColumnName; + while (_baseTable.Columns.Contains(colName)) { suffix++; - colname = suffix > 0 ? string.Format("{0}_{1}", dbf.ColumnName, suffix) : dbf.ColumnName; + colName = suffix > 0 ? string.Format(CultureInfo.CurrentCulture, "{0}_{1}", dbf.ColumnName, suffix) : dbf.ColumnName; } - _baseTable.Columns.Add(colname, TypeByTypeCode(dbf.DataTypeCode)); + _baseTable.Columns.Add(colName, TypeByTypeCode(dbf.DataTypeCode)); } } @@ -847,18 +733,19 @@ public Encoding Encoding set { //Test if encoding is the same as the one gathered by GetLanguageDriverId - if (value == _fileEncoding) + if (ReferenceEquals(value, _fileEncoding)) value = null; //Since objects are not the same instances, try comparison if (value != null && value.Equals(_fileEncoding)) value = null; - if (value != _encoding) - { - _encoding = value; - OnEncodingChanged(EventArgs.Empty); - } + if (ReferenceEquals(value, _encoding)) + return; + + _encoding = value; + OnEncodingChanged(EventArgs.Empty); + } } @@ -901,9 +788,7 @@ internal FeatureDataRow GetFeature(uint oid, FeatureDataTable table) private object ReadDbfValue(DbaseField dbf) { - var tmpBuffer = new byte[dbf.Length]; - Buffer.BlockCopy(_currentRecordBuffer, dbf.Address+1, tmpBuffer, 0, dbf.Length); - + var tmpBuffer = new ReadOnlySpan(_currentRecordBuffer, dbf.Address + 1, dbf.Length); string temp; switch (dbf.DataTypeCode) @@ -914,67 +799,60 @@ private object ReadDbfValue(DbaseField dbf) : _encoding.GetString(tmpBuffer).Replace("\0", "").Trim(); case TypeCode.Double: - temp = Encoding.UTF7.GetString(tmpBuffer).Replace("\0", "").Trim(); - double dbl; - if (double.TryParse(temp, NumberStyles.Float, Nfi, out dbl)) + temp = Encoding.UTF8.GetString(tmpBuffer).Replace("\0", ""); + if (double.TryParse(temp, NumberStyles.Float, Nfi, out double dbl)) return dbl; return DBNull.Value; - case TypeCode.Byte: - temp = Encoding.UTF7.GetString(tmpBuffer).Replace("\0", "").Trim(); - Byte i8; - if (Byte.TryParse(temp, NumberStyles.Integer, Nfi, out i8)) + case TypeCode.SByte: + temp = Encoding.UTF8.GetString(tmpBuffer).Replace("\0", ""); + if (sbyte.TryParse(temp, NumberStyles.Integer, Nfi, out sbyte i8)) return i8; return DBNull.Value; case TypeCode.Int16: - temp = Encoding.UTF7.GetString(tmpBuffer).Replace("\0", "").Trim(); - Int16 i16; - if (Int16.TryParse(temp, NumberStyles.Integer, Nfi, out i16)) + temp = Encoding.UTF8.GetString(tmpBuffer).Replace("\0", ""); + if (short.TryParse(temp, NumberStyles.Integer, Nfi, out short i16)) return i16; return DBNull.Value; case TypeCode.Int32: - temp = Encoding.UTF7.GetString(tmpBuffer).Replace("\0", "").Trim(); - Int32 i32; - if (Int32.TryParse(temp, NumberStyles.Integer, Nfi, out i32)) + temp = Encoding.UTF8.GetString(tmpBuffer).Replace("\0", ""); + if (int.TryParse(temp, NumberStyles.Integer, Nfi, out int i32)) return i32; return DBNull.Value; case TypeCode.Int64: - temp = Encoding.UTF7.GetString(tmpBuffer).Replace("\0", "").Trim(); - Int64 i64; - if (Int64.TryParse(temp, NumberStyles.Integer, Nfi, out i64)) + temp = Encoding.UTF8.GetString(tmpBuffer).Replace("\0", ""); + if (long.TryParse(temp, NumberStyles.Integer, Nfi, out long i64)) return i64; return DBNull.Value; //case "System.Single": case TypeCode.Single: - temp = Encoding.UTF8.GetString(tmpBuffer); - float f; - if (float.TryParse(temp, NumberStyles.Float, Nfi, out f)) + temp = Encoding.UTF8.GetString(tmpBuffer).Replace("\0", ""); + if (float.TryParse(temp, NumberStyles.Float, Nfi, out float f)) return f; return DBNull.Value; //case "System.Boolean": case TypeCode.Boolean: - var tempChar = BitConverter.ToChar(tmpBuffer, 0); - return ((tempChar == 'T') || (tempChar == 't') || (tempChar == 'Y') || (tempChar == 'y')); + return (tmpBuffer[0] == 'T' || tmpBuffer[0] == 't' || + tmpBuffer[0] == 'Y' || tmpBuffer[0] == 'y'); //case "System.DateTime": case TypeCode.DateTime: - DateTime date; // Mono has not yet implemented DateTime.TryParseExact #if !MONO - if (DateTime.TryParseExact(Encoding.UTF7.GetString(tmpBuffer), - "yyyyMMdd", Nfi, DateTimeStyles.None, out date)) + if (DateTime.TryParseExact(Encoding.UTF8.GetString(tmpBuffer), + "yyyyMMdd", Nfi, DateTimeStyles.None, out var date)) return date; return DBNull.Value; #else try { - return date = DateTime.ParseExact ( System.Text.Encoding.UTF7.GetString(tmpBuffer), - "yyyyMMdd", Nfi, System.Globalization.DateTimeStyles.None ); + return DateTime.ParseExact ( System.Text.Encoding.UTF8.GetString(tmpBuffer), + "yyyyMMdd", Nfi, System.Globalization.DateTimeStyles.None ); } catch ( Exception e ) { @@ -983,89 +861,20 @@ private object ReadDbfValue(DbaseField dbf) #endif default: throw (new NotSupportedException("Cannot parse DBase field '" + dbf.ColumnName + "' of type '" + -// dbf.DataType + "'")); TypeByTypeCode(dbf.DataTypeCode) + "'")); } - -// switch (dbf.DataTypeCode) -// { -// case TypeCode.String: -// return _encoding == null -// ? _fileEncoding.GetString(br.ReadBytes(dbf.Length)).Replace("\0", "").Trim() -// : _encoding.GetString(br.ReadBytes(dbf.Length)).Replace("\0", "").Trim(); -// case TypeCode.Double: -// temp = Encoding.UTF7.GetString(br.ReadBytes(dbf.Length)).Replace("\0", "").Trim(); -// double dbl; -// if (double.TryParse(temp, NumberStyles.Float, Nfi, out dbl)) -// return dbl; -// return DBNull.Value; -// case TypeCode.Int16: -// temp = Encoding.UTF7.GetString((br.ReadBytes(dbf.Length))).Replace("\0", "").Trim(); -// Int16 i16; -// if (Int16.TryParse(temp, NumberStyles.Integer, Nfi, out i16)) -// return i16; -// return DBNull.Value; -// case TypeCode.Int32: -// temp = Encoding.UTF7.GetString((br.ReadBytes(dbf.Length))).Replace("\0", "").Trim(); -// Int32 i32; -// if (Int32.TryParse(temp, NumberStyles.Integer, Nfi, out i32)) -// return i32; -// return DBNull.Value; -// case TypeCode.Int64: -// temp = Encoding.UTF7.GetString((br.ReadBytes(dbf.Length))).Replace("\0", "").Trim(); -// Int64 i64; -// if (Int64.TryParse(temp, NumberStyles.Integer, Nfi, out i64)) -// return i64; -// return DBNull.Value; -// //case "System.Single": -// case TypeCode.Single: -// temp = Encoding.UTF8.GetString((br.ReadBytes(dbf.Length))); -// float f; -// if (float.TryParse(temp, NumberStyles.Float, Nfi, out f)) -// return f; -// return DBNull.Value; -// //case "System.Boolean": -// case TypeCode.Boolean: -// var tempChar = br.ReadChar(); -// return ((tempChar == 'T') || (tempChar == 't') || (tempChar == 'Y') || (tempChar == 'y')); -// //case "System.DateTime": -// case TypeCode.DateTime: -// DateTime date; -// // Mono has not yet implemented DateTime.TryParseExact -//#if !MONO -// if (DateTime.TryParseExact(Encoding.UTF7.GetString((br.ReadBytes(8))), -// "yyyyMMdd", Nfi, DateTimeStyles.None, out date)) -// return date; -// return DBNull.Value; -//#else -// try -// { -// return date = DateTime.ParseExact ( System.Text.Encoding.UTF7.GetString((br.ReadBytes(8))), -// "yyyyMMdd", SharpMap.Map.numberFormat_EnUS, System.Globalization.DateTimeStyles.None ); -// } -// catch ( Exception e ) -// { -// return DBNull.Value; -// } -//#endif -// default: -// throw (new NotSupportedException("Cannot parse DBase field '" + dbf.ColumnName + "' of type '" + -// // dbf.DataType + "'")); -// TypeByTypeCode(dbf.DataTypeCode) + "'")); -// } - } /// /// Gets all attribute values for data record /// - /// + /// An array of attribute values public object[] GetValues(uint oid) { object[] result; - var offset = 0; + int offset = 0; - //Preprare result buffer + // Prepare result buffer if (IncludeOid) { offset = 1; @@ -1077,14 +886,14 @@ public object[] GetValues(uint oid) result = new object[_dbaseColumns.Length]; } - //Fill result buffer + // Fill result buffer if (!RecordDeleted(oid)) { - for (var i = 0; i < _dbaseColumns.Length; i++) + for (int i = 0; i < _dbaseColumns.Length; i++) result[i + offset] = GetValue(oid, i); } return result; } } -} \ No newline at end of file +} From 415c85e57114c040a8a502bab18f275c8c0ba8e4 Mon Sep 17 00:00:00 2001 From: Felix Obermaier Date: Mon, 11 Apr 2022 16:03:23 +0200 Subject: [PATCH 2/7] Remove method lock from LegacyMapImageRenderer.MergeImages refers to #229 --- SharpMap.UI/Forms/ImageGenerator/LegacyMapImageRenderer.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/SharpMap.UI/Forms/ImageGenerator/LegacyMapImageRenderer.cs b/SharpMap.UI/Forms/ImageGenerator/LegacyMapImageRenderer.cs index 523047ff..f916c2ca 100644 --- a/SharpMap.UI/Forms/ImageGenerator/LegacyMapImageRenderer.cs +++ b/SharpMap.UI/Forms/ImageGenerator/LegacyMapImageRenderer.cs @@ -416,7 +416,6 @@ private void GetImagesAsyncEnd(GetImageEndResult res) } } - [MethodImpl(MethodImplOptions.Synchronized)] private Bitmap MergeImages(long generation, Rectangle rectangle) { var res = generation == _idImageGeneration From 6b13ff29d726caad0c2e6633996e1ad044cd119d Mon Sep 17 00:00:00 2001 From: Felix Obermaier Date: Mon, 11 Apr 2022 16:43:28 +0200 Subject: [PATCH 3/7] Remove recursive patterns --- SharpMap/Data/Providers/DbaseReader.cs | 168 ++++++++++++++----------- 1 file changed, 95 insertions(+), 73 deletions(-) diff --git a/SharpMap/Data/Providers/DbaseReader.cs b/SharpMap/Data/Providers/DbaseReader.cs index b9d6b250..2ed60322 100644 --- a/SharpMap/Data/Providers/DbaseReader.cs +++ b/SharpMap/Data/Providers/DbaseReader.cs @@ -334,18 +334,31 @@ private void ParseDbfHeader(string filename) _dbaseColumns[i] = new DbaseField(); _dbaseColumns[i].ColumnName = _fileEncoding.GetString(br.ReadBytes(11)).Replace("\0", "").Trim(); char fieldType = br.ReadChar(); - _dbaseColumns[i].DataTypeCode = fieldType switch + switch (fieldType) { - 'L' => TypeCode.Boolean, - 'C' => TypeCode.String, - 'D' => TypeCode.DateTime, - 'N' => TypeCode.Double, - 'F' => TypeCode.Single, - 'B' => TypeCode.Object //Hack for - , - _ => throw new NotSupportedException("Invalid or unknown DBase field type '" + fieldType + - "' in column '" + _dbaseColumns[i].ColumnName + "'") - }; + case 'L': + _dbaseColumns[i].DataTypeCode = TypeCode.Boolean; + break; + case 'C': + _dbaseColumns[i].DataTypeCode = TypeCode.String; + break; + case 'D': + _dbaseColumns[i].DataTypeCode = TypeCode.DateTime; + break; + case 'N': + _dbaseColumns[i].DataTypeCode = TypeCode.Double; + break; + case 'F': + _dbaseColumns[i].DataTypeCode = TypeCode.Single; + break; + case 'B': + _dbaseColumns[i].DataTypeCode = TypeCode.Object; + break; + default: + throw new NotSupportedException("Invalid or unknown DBase field type '" + fieldType + + "' in column '" + _dbaseColumns[i].ColumnName + "'"); + } + _dbaseColumns[i].Address = br.ReadInt32(); if (i == 0) //Set it to 0 for first column and calculate it for the rest _dbaseColumns[i].Address = 0; @@ -712,10 +725,16 @@ public FeatureDataTable NewTable private byte[] _currentRecordBuffer; private uint _currentRecordOid = 0xffffffff; - internal object GetValue(uint oid, int colid) + /// + /// Gets the attribute value for -column + /// + /// The feature id + /// The column id + /// The attribute value + internal object GetValue(uint oid, int colId) { CurrentRecordOid = oid; - return ReadDbfValue(_dbaseColumns[colid]); + return ReadDbfValue(_dbaseColumns[colId]); } private Encoding _encoding; @@ -786,68 +805,70 @@ internal FeatureDataRow GetFeature(uint oid, FeatureDataTable table) private static readonly NumberFormatInfo Nfi = NumberFormatInfo.InvariantInfo; - private object ReadDbfValue(DbaseField dbf) + private unsafe object ReadDbfValue(DbaseField dbf) { var tmpBuffer = new ReadOnlySpan(_currentRecordBuffer, dbf.Address + 1, dbf.Length); - string temp; - - switch (dbf.DataTypeCode) + fixed (byte* tmpPointer = tmpBuffer) { - case TypeCode.String: - return _encoding == null - ? _fileEncoding.GetString(tmpBuffer).Replace("\0", "").Trim() - : _encoding.GetString(tmpBuffer).Replace("\0", "").Trim(); - - case TypeCode.Double: - temp = Encoding.UTF8.GetString(tmpBuffer).Replace("\0", ""); - if (double.TryParse(temp, NumberStyles.Float, Nfi, out double dbl)) - return dbl; - return DBNull.Value; - - case TypeCode.SByte: - temp = Encoding.UTF8.GetString(tmpBuffer).Replace("\0", ""); - if (sbyte.TryParse(temp, NumberStyles.Integer, Nfi, out sbyte i8)) - return i8; - return DBNull.Value; + string temp; - case TypeCode.Int16: - temp = Encoding.UTF8.GetString(tmpBuffer).Replace("\0", ""); - if (short.TryParse(temp, NumberStyles.Integer, Nfi, out short i16)) - return i16; - return DBNull.Value; - - case TypeCode.Int32: - temp = Encoding.UTF8.GetString(tmpBuffer).Replace("\0", ""); - if (int.TryParse(temp, NumberStyles.Integer, Nfi, out int i32)) - return i32; - return DBNull.Value; - - case TypeCode.Int64: - temp = Encoding.UTF8.GetString(tmpBuffer).Replace("\0", ""); - if (long.TryParse(temp, NumberStyles.Integer, Nfi, out long i64)) - return i64; - return DBNull.Value; - - //case "System.Single": - case TypeCode.Single: - temp = Encoding.UTF8.GetString(tmpBuffer).Replace("\0", ""); - if (float.TryParse(temp, NumberStyles.Float, Nfi, out float f)) - return f; - return DBNull.Value; - - //case "System.Boolean": - case TypeCode.Boolean: - return (tmpBuffer[0] == 'T' || tmpBuffer[0] == 't' || - tmpBuffer[0] == 'Y' || tmpBuffer[0] == 'y'); - - //case "System.DateTime": - case TypeCode.DateTime: - // Mono has not yet implemented DateTime.TryParseExact + switch (dbf.DataTypeCode) + { + case TypeCode.String: + return _encoding == null + ? _fileEncoding.GetString(tmpPointer, tmpBuffer.Length).Replace("\0", "").Trim() + : _encoding.GetString(tmpPointer, tmpBuffer.Length).Replace("\0", "").Trim(); + + case TypeCode.Double: + temp = Encoding.UTF8.GetString(tmpPointer, tmpBuffer.Length).Replace("\0", ""); + if (double.TryParse(temp, NumberStyles.Float, Nfi, out double dbl)) + return dbl; + return DBNull.Value; + + case TypeCode.SByte: + temp = Encoding.UTF8.GetString(tmpPointer, tmpBuffer.Length).Replace("\0", ""); + if (sbyte.TryParse(temp, NumberStyles.Integer, Nfi, out sbyte i8)) + return i8; + return DBNull.Value; + + case TypeCode.Int16: + temp = Encoding.UTF8.GetString(tmpPointer, tmpBuffer.Length).Replace("\0", ""); + if (short.TryParse(temp, NumberStyles.Integer, Nfi, out short i16)) + return i16; + return DBNull.Value; + + case TypeCode.Int32: + temp = Encoding.UTF8.GetString(tmpPointer, tmpBuffer.Length).Replace("\0", ""); + if (int.TryParse(temp, NumberStyles.Integer, Nfi, out int i32)) + return i32; + return DBNull.Value; + + case TypeCode.Int64: + temp = Encoding.UTF8.GetString(tmpPointer, tmpBuffer.Length).Replace("\0", ""); + if (long.TryParse(temp, NumberStyles.Integer, Nfi, out long i64)) + return i64; + return DBNull.Value; + + //case "System.Single": + case TypeCode.Single: + temp = Encoding.UTF8.GetString(tmpPointer, tmpBuffer.Length).Replace("\0", ""); + if (float.TryParse(temp, NumberStyles.Float, Nfi, out float f)) + return f; + return DBNull.Value; + + //case "System.Boolean": + case TypeCode.Boolean: + return (tmpBuffer[0] == 'T' || tmpBuffer[0] == 't' || + tmpBuffer[0] == 'Y' || tmpBuffer[0] == 'y'); + + //case "System.DateTime": + case TypeCode.DateTime: + // Mono has not yet implemented DateTime.TryParseExact #if !MONO - if (DateTime.TryParseExact(Encoding.UTF8.GetString(tmpBuffer), - "yyyyMMdd", Nfi, DateTimeStyles.None, out var date)) - return date; - return DBNull.Value; + if (DateTime.TryParseExact(Encoding.UTF8.GetString(tmpPointer, tmpBuffer.Length), + "yyyyMMdd", Nfi, DateTimeStyles.None, out var date)) + return date; + return DBNull.Value; #else try { @@ -859,9 +880,10 @@ private object ReadDbfValue(DbaseField dbf) return DBNull.Value; } #endif - default: - throw (new NotSupportedException("Cannot parse DBase field '" + dbf.ColumnName + "' of type '" + - TypeByTypeCode(dbf.DataTypeCode) + "'")); + default: + throw (new NotSupportedException("Cannot parse DBase field '" + dbf.ColumnName + "' of type '" + + TypeByTypeCode(dbf.DataTypeCode) + "'")); + } } } From a227879be7fbcd141a4e2ccb2caa0b3de54b570e Mon Sep 17 00:00:00 2001 From: Felix Obermaier Date: Mon, 11 Apr 2022 16:52:34 +0200 Subject: [PATCH 4/7] Allow unsafe --- SharpMap/SharpMap.csproj | 2 ++ 1 file changed, 2 insertions(+) diff --git a/SharpMap/SharpMap.csproj b/SharpMap/SharpMap.csproj index da7b8c48..0cafd443 100644 --- a/SharpMap/SharpMap.csproj +++ b/SharpMap/SharpMap.csproj @@ -10,6 +10,7 @@ This package contains the core components of SharpMap. 1701;1702;2100;2235 + true @@ -28,6 +29,7 @@ + From e1cc235cdde58519126048bf0161ea484f8e9753 Mon Sep 17 00:00:00 2001 From: Felix Obermaier Date: Thu, 14 Apr 2022 10:54:32 +0200 Subject: [PATCH 5/7] Catch WebException in unit test --- UnitTests/Data/Providers/KmlProviderTests.cs | 25 ++++++++++++++------ 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/UnitTests/Data/Providers/KmlProviderTests.cs b/UnitTests/Data/Providers/KmlProviderTests.cs index 9d78a045..30261404 100644 --- a/UnitTests/Data/Providers/KmlProviderTests.cs +++ b/UnitTests/Data/Providers/KmlProviderTests.cs @@ -134,16 +134,27 @@ public void TestHttp(string kmlWebResource, string description) KmlProvider p = null; var request = WebRequest.Create(kmlWebResource); - using (var response = request.GetResponse()) + try { - if (response.ContentType == "None" || response.ContentType.Contains("vnd.google-earth.kml")) + using (var response = request.GetResponse()) { - var s = response.GetResponseStream(); - p = KmlProvider.FromKml(s); + if (response.ContentType == "None" || response.ContentType.Contains("vnd.google-earth.kml")) + { + var s = response.GetResponseStream(); + p = KmlProvider.FromKml(s); + } + + else if (response.ContentType.Contains("vnd.google-earth.kmz")) + p = KmlProvider.FromKmz(response.GetResponseStream(), "doc.kml"); } - - else if (response.ContentType.Contains("vnd.google-earth.kmz")) - p = KmlProvider.FromKmz(response.GetResponseStream(), "doc.kml"); + } + catch (WebException ex) + { + Assert.Ignore(ex.Message); + } + catch (Exception ex) + { + Assert.Fail(ex.Message); } var l = new VectorLayer(p.ConnectionID, p); From 0421e3c10cdd82a1dae8818f778f4c7a82598441 Mon Sep 17 00:00:00 2001 From: Felix Obermaier Date: Thu, 21 Apr 2022 10:58:21 +0200 Subject: [PATCH 6/7] Improve transfrom of Envelope --- .../Transformations/GeometryTransform.cs | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/SharpMap/CoordinateSystems/Transformations/GeometryTransform.cs b/SharpMap/CoordinateSystems/Transformations/GeometryTransform.cs index db0a9b6a..66f06fd8 100644 --- a/SharpMap/CoordinateSystems/Transformations/GeometryTransform.cs +++ b/SharpMap/CoordinateSystems/Transformations/GeometryTransform.cs @@ -22,7 +22,7 @@ namespace GeoAPI.CoordinateSystems.Transformations { /// - /// Helper class for transforming + /// Helper class for transforming /// public class GeometryTransform { @@ -40,16 +40,14 @@ public static Envelope TransformBox(Envelope box, IMathTransform transform) if (box.IsNull) return new Envelope(box); - var corners = new[] { - transform.Transform(new Coordinate(box.MinX, box.MinY)), - transform.Transform(new Coordinate(box.MinX, box.MaxY)), - transform.Transform(new Coordinate(box.MaxX, box.MinY)), - transform.Transform(new Coordinate(box.MaxX, box.MaxY)) }; + var factory = GeometryServiceProvider.Instance.CreateGeometryFactory(); + var ring = factory.ToGeometry(box); + ring = NetTopologySuite.Densify.Densifier.Densify(ring, ring.Length / 100d); + ring = TransformGeometry(ring, transform, factory); - var result = new Envelope(corners[0]); - for (var i = 1; i < 4; i++) - result.ExpandToInclude(corners[i]); - return result; + var res = ring.EnvelopeInternal; + res.ExpandToInclude(transform.Transform(box.Centre)); + return res; } /// From 1d0f0efc56a9c1f0478eaa3f1306877dcbfff32e Mon Sep 17 00:00:00 2001 From: Felix Obermaier Date: Thu, 21 Apr 2022 11:04:01 +0200 Subject: [PATCH 7/7] Improve parsing of wkt for DotSpatialCoordinateSystemFactory * null argument check for DotSpatialCoordinateTransformationFactory --- .../DotSpatialCoordinateSystemFactory.cs | 21 ++++++++++++------- ...tSpatialCoordinateTransformationFactory.cs | 3 +++ 2 files changed, 16 insertions(+), 8 deletions(-) diff --git a/SharpMap.CoordinateSystems.DotSpatial/DotSpatialCoordinateSystemFactory.cs b/SharpMap.CoordinateSystems.DotSpatial/DotSpatialCoordinateSystemFactory.cs index c5b485d3..32b00faa 100644 --- a/SharpMap.CoordinateSystems.DotSpatial/DotSpatialCoordinateSystemFactory.cs +++ b/SharpMap.CoordinateSystems.DotSpatial/DotSpatialCoordinateSystemFactory.cs @@ -98,14 +98,19 @@ public ICoordinateSystem CreateFromXml(string xml) public ICoordinateSystem CreateFromWkt(string wkt) { //Hack: DotSpatial.Projections does not handle Authority and AuthorityCode - var pos1 = 10 + wkt.LastIndexOf("AUTHORITY[", StringComparison.InvariantCulture); - var pos2 = wkt.IndexOf("]", pos1, StringComparison.InvariantCulture) - 1; - var parts = wkt.Substring(pos1, pos2 - pos1 + 1).Split(','); - - var auth = parts[0].Replace("\"", "").Trim(); - var code = int.Parse(parts[1].Replace("\"", ""), NumberStyles.Integer, - NumberFormatInfo.InvariantInfo); + var pos1 = wkt.LastIndexOf("AUTHORITY[", StringComparison.InvariantCulture); + string auth = "EPSG"; + int code = 0; + // If there is an Authority entry in the WKT, try to parse the values + if (pos1 >= 0) + { + pos1 += 10; + var pos2 = wkt.IndexOf("]", pos1, StringComparison.InvariantCulture) - 1; + var parts = wkt.Substring(pos1, pos2 - pos1 + 1).Split(','); + auth = parts[0].Replace("\"", "").Trim(); + int.TryParse(parts[1].Replace("\"", ""), NumberStyles.Integer, NumberFormatInfo.InvariantInfo, out code); + } ProjectionInfo pi = null; try { @@ -270,4 +275,4 @@ public IVerticalDatum CreateVerticalDatum(string name, DatumType datumType) throw new NotSupportedException(); } } -} \ No newline at end of file +} diff --git a/SharpMap.CoordinateSystems.DotSpatial/Transformations/DotSpatialCoordinateTransformationFactory.cs b/SharpMap.CoordinateSystems.DotSpatial/Transformations/DotSpatialCoordinateTransformationFactory.cs index 12b11438..d266e1cb 100644 --- a/SharpMap.CoordinateSystems.DotSpatial/Transformations/DotSpatialCoordinateTransformationFactory.cs +++ b/SharpMap.CoordinateSystems.DotSpatial/Transformations/DotSpatialCoordinateTransformationFactory.cs @@ -27,6 +27,9 @@ public class DotSpatialCoordinateTransformationFactory : ICoordinateTransformati /// public ICoordinateTransformation CreateFromCoordinateSystems(ICoordinateSystem sourceCS, ICoordinateSystem targetCS) { + if (sourceCS == null || targetCS == null) + return null; + var source = sourceCS as DotSpatialCoordinateSystem ?? new DotSpatialCoordinateSystem(ProjectionInfo.FromEsriString(sourceCS.WKT));