在SqlServer等结构化的数据库中通常使用geometry 或者是 geoGraphy 。在数据中的类型如下:
.NET对其操作 通过使用EF中的DbGeometry类型进行映射,并且包含对其的多种属性和操作。基本可以满足我们通用的需求。
在EF中,该类型的定义如下:
这虽然满足,.net程序,对于空间数据的基本使用和操作。但是由于目前我们使用的数据存储服务器为ElasticSearch。其中定义空间数据包含两种类型,分别是geoShape和geoPoint两种类型。
我们想要实现数据库中空间类型 和 elasticsearch中空间类型的转化显得非常困难。因为两种类型的结构和形式完全不同。
查找多方资料,终于知道ElasticSearch是一种geoJson类型的。因此需要先将geometry转成 geoJson ,geoJson再转成geoShape。
下面是转化的步骤:
在需要使用geoJson类型的项目,右键点击“管理NuGet程序包”,搜索geoJson.net,出现的结果中选择geoJson.net 选择安装即可。
最后在已安装的包中查看如下:
需要注意的是,geoJson依赖的newtnsoft.json的版本是7.0以上,所以很有可能和当前项目冲突。可以通过修改配置文件的版本进行解决。
具体转化的代码如下,需要引用geoJson才可使用哦
using System;
using System.Data.Entity.Spatial;
using System.Linq;
using GeoJSON.Net.Geometry;
using Newtonsoft.Json;
namespace Jurassic.Sooil.Model.IOModel
{
public class GisUtil
{
/**
* 将WKT文本字符串转换为ES中的GeoShape字符串格式
* @param wkt
* @return
* @throws ParseException
*/
public static string GetEsGeoTextFromWkt(DbGeometry geo)
{
string result = null;
string coordinates = null;
var type = geo.SpatialTypeName.ToLower();
if (type.Equals("point"))
{
coordinates = GetEsPointText(geo);
}
else
{
coordinates = GetEsMultiPointText(geo);
}
result += "{\"type\" :\"" + type + "\"" + ", \"coordinates\" :" + coordinates + "}";
return result;
}
/**
* 通过MultiPoint对象拼接中括号表示的字符串
* @param multiPoint
* @return
*/
private static String GetEsMultiPointText(DbGeometry polygon)
{
var wkt = polygon.WellKnownValue.WellKnownText;
var startIndex = wkt.IndexOf('(');
var endIndex = wkt.LastIndexOf(')');
var coordinates = wkt.Substring(startIndex, endIndex - startIndex + 1);
var coods = coordinates.Replace('(', '[').Replace(')', ']').Split(',');
var result = "";
foreach (var cood in coods)
{
var s= cood.TrimStart().Replace(' ', ',');
result += string.Format("[" + s + "],");
}
result= result.Remove(result.Length - 1);
return result;
}
/**
* 通过Point对象拼接中括号表示的字符串
* @param point
* @return
*/
public static String GetEsPointText(DbGeometry point)
{
return "[" + point.XCoordinate + "," + point.YCoordinate + "]";
}
public static dynamic GetGeoJson(DbGeometry geo)
{
var geoText = GetEsGeoTextFromWkt(geo);
var type = geo.SpatialTypeName.ToLower();
switch (type)
{
case "point": return JsonConvert.DeserializeObject(geoText);
case "polygon": return JsonConvert.DeserializeObject(geoText);
case "multipolygon": return JsonConvert.DeserializeObject(geoText);
case "multilinestring": return JsonConvert.DeserializeObject(geoText);
default: return null;
}
}
}
}
Spatial =GisUtil.GetGeoJson(DbGeometry.FromText("POLYGON ((125.718299865723 45.7559700012207, 125.700149536133 45.761962890625, 125.68383026123 45.7523498535156, 125.672470092773 45.7415008544922, 125.667694091797 45.729377746582, 125.664573669434 45.7161026000977, 125.681015014648 45.7145957946777, 125.71363067627 45.7338333129883, 125.716812133789 45.7426528930664, 125.721588134766 45.7547760009766, 125.718299865723 45.7559700012207))", 4326))
也就是说如何把转化后的数据导入到ElasticSearch中去。
非常幸运,我们做到这一步,基本就可以直接导入到ElasticSearch中去了,因为geoJson的格式和geoShape的格式是一摸一样的。
下面是bulk代码
///
/// 修改数据模型模型后,导入ES的数据
///
///
///
///
public async Task Index(List docs, string type)
{
var serializer = new JsonNetSerializer();
var bulkJson =
new BulkBuilder(serializer)
.BuildCollection(docs,
(builder, doc) => builder.Index(doc)
);
return await Bulk(bulkJson, type);
}
///
/// bulk操作,根据指定的索引和类型
///
///
///
///
public async Task Bulk(string bulkJson, string type)
{
if (bulkJson == null) return "索引文档为空";
string bulkCommand = new BulkCommand(EsIndex, type);
var result = await esClient.Post(bulkCommand, bulkJson);
return result;
}
通过以上步骤,就解决了ES中geoShape空间数据导入的难题。不过geometry到geojson的转化还是有些不好,需要人工去解析和转化。期待更好的解决方案!