I have no idea why importing shape files should be such a twisted, complicated task. I ended up using SharpMap library, using only GeoAPI, NetTopologySuite, ProjNet, and SharpMap DLL files in a console application, to import it via C# code.
For future reference this is my code, and explanations are afterwards:
class Program
{
static void Main(string[] args)
{
args = new string[] { @"D:\Temp\iran-latest-free.shp\gis.osm_places_free_1.shp" };
EnsureArgs(args);
Initialize();
var shapeFile = new ShapeFile(args[0]);
shapeFile.Open();
if (shapeFile.GetFeatureCount() == 0)
{
throw new FrameworkException("No records found to be imported");
}
string tableName = CreateTable(shapeFile);
InsertRecords(shapeFile, tableName);
}
private static void InsertRecords(ShapeFile shapeFile, string tableName)
{
var count = shapeFile.GetFeatureCount();
int counter = 0;
for (int i = 0; i < count; i++)
{
var feature = shapeFile.GetFeature((uint)i);
var columns = shapeFile.GetFeature((uint)i).Table.Columns;
var columnsScript = "";
foreach (DataColumn column in columns)
{
columnsScript += "[{0}],".Fill(column.Caption);
}
columnsScript = columnsScript.Trim().Trim(',');
columnsScript += ", [Geo]";
var valuesScript = "";
var values = feature.ItemArray.Select(x => x.ToString());
foreach (var value in values)
{
valuesScript += "N'{0}', ".Fill(value.Replace("'", "''"));
}
valuesScript = valuesScript.Trim().Trim(',');
valuesScript += ", geometry::STGeomFromText('{0}', 4326)".Fill(feature.Geometry.AsText());
var query = @"
insert into {0} ({1})
values ({2})
".Fill(tableName, columnsScript, valuesScript);
Database.Open(RepositoryFactory.ConfigurationRepository.Context.Database.Connection.ConnectionString).Run(query);
Logger.Count(counter++);
}
}
private static string CreateTable(ShapeFile shapeFile)
{
var columns = shapeFile.GetFeature(0).Table.Columns;
var columnsScript = "";
foreach (DataColumn column in columns)
{
columnsScript += "\r\n" + column.Caption + " nvarchar(max),";
}
//columnsScript = columnsScript.Trim().Trim(',');
string tableName = "Temp" + RandomHelper.GenerateAlphanumericToken(10);
var tableScript = @"
create table {0}
(
{1}
Geo geometry
)
".Fill(tableName, columnsScript);
Database.Open(RepositoryFactory.ConfigurationRepository.Context.Database.Connection.ConnectionString).Run(tableScript);
return tableName;
}
private static void Initialize()
{
var gss = new NtsGeometryServices();
var css = new SharpMap.CoordinateSystems.CoordinateSystemServices(
new CoordinateSystemFactory(),
new CoordinateTransformationFactory(),
SharpMap.Converters.WellKnownText.SpatialReference.GetAllReferenceSystems());
GeoAPI.GeometryServiceProvider.Instance = gss;
SharpMap.Session.Instance
.SetGeometryServices(gss)
.SetCoordinateSystemServices(css)
.SetCoordinateSystemRepository(css);
}
private static void EnsureArgs(string[] args)
{
if (args.Length == 0)
{
throw new FrameworkException("Arguments are not provided");
}
var file = args[0];
if (!File.Exists(file))
{
throw new FrameworkException("File {0} doesn't exist".Fill(file));
}
}
}
Disclaimer: This code is done by me, without truly knowing what I'm doing. I just know that GIS is based on data, and I need to insert some data for each point in SQL Server. Database is my own utility wrapping ADO.NET's functionality. Fill is a utility used instead of string.Format. This code might break for you. It's just here to help people like me know more about tools available to them. Also all fields are imported as nvarchar(max) for the least possibility of any data type problems. Table is created on the fly via a RandomHelper class which is also mine, and just creates a random token.