How to train model with TensorflowSharp - c#

I am new with TensorfolwSharp. I see some examples about how to load model and predict using TensorfolwSharp. But I cannot find examples to train model using TensorfolwSharp. I give it a try and stuck at Optimizer now. Too many parameters for ApplyAdam, not even sure this is the right function to use.
Following is my WORKING Tensorflow code.
dataX = pd.read_csv('dataX.csv', sep = ',', header = None)
dataX = pd.read_csv('dataY.csv', sep = ',', header = None)
x = tf.placeholder(tf.float32, [None, trainX.shape[1]])
y = tf.placeholder(tf.float32, [None, 1])
W0 = tf.Variable(tf.random_normal([trainX.shape[1], h0size], seed = seed))
b0 = tf.Variable(tf.random_normal([h0size], seed = seed))
h = tf.matmul(x, W0) + b0
pred = tf.sigmoid(h)
cost = tf.reduce_mean(tf.nn.sigmoid_cross_entropy_with_logits(labels = y, logits = h))
optimizer = tf.train.AdamOptimizer(learning_rate).minimize(cost)
with tf.Session() as sess:
sess.run(init)
for epoch in range(50):
_, c = sess.run([optimizer, cost], feed_dict ={x: dataX, y: dataX})
Following is my attemp to convert to TensorflowSharp
using (var session = new TFSession())
{
var graph = session.Graph;
//dataX = pd.read_csv('dataX.csv', sep = ',', header = None)
//dataY = pd.read_csv('dataY.csv', sep = ',', header = None)
float[,] aX = LoadCsv("dataX.csv");
float[,] aY = LoadCsv("dataY.csv");
TFTensor dataX = new TFTensor(aX);
TFTensor dataY = new TFTensor(aY);
//x = tf.placeholder(tf.float32, [None, trainX.shape[1]])
//y = tf.placeholder(tf.float32, [None, 1])
var x = graph.Placeholder(TFDataType.Float, new TFShape(dataX.Shape));
var y = graph.Placeholder(TFDataType.Float, new TFShape(dataY.Shape));
var W0 = graph.Variable(graph.RandomNormal(new TFShape(dataX.Shape[1], 1)));
var b0 = graph.Variable(graph.RandomNormal(new TFShape(1)));
var h = graph.Add(graph.MatMul(x, W0), b0);
var pred = graph.Sigmoid(h);
var cost = graph.ReduceMean(graph.SigmoidCrossEntropyWithLogits(y,h));
//optimizer = tf.train.AdamOptimizer(learning_rate).minimize(cost)
// Too many parameters for ApplyAdam, not even sure this is the right function to use.
TFOutput var, m = 0, v = 0, beta1_power, beta2_power, lr = 0.01, beta1 = 0.9, beta2 = 0.999, epsilon = 0.00000001, grad;
var optimizer = graph.ApplyAdam(var, m, v, beta1_power, beta2_power, lr, beta1, beta2, epsilon, grad);
for (int i = 0; i < 50 ; i++)
session.GetRunner().Run(
inputs: new[] { optimizer },
inputValues: new[] { dataX, dataY }
);
}

TensorFlow API doesn't provide optimizer classes yet. So to minimize cost function you should calculate gradients using API and then manually update trainable parameters of your model. Here is an example with separate classes for convinience:
class Model : IDisposable
{
TFSession _session;
TFGraph _graph;
TFOutput _input;
TFOutput _output;
LinearLayer _y_out;
TFOutput _cost;
TFTensor _dataX;
TFTensor _dataY;
GradientDescentOptimizer _gradientDescentOptimizer;
public Model()
{
float[,] aX = LoadCsv("dataX.csv");
float[,] aY = LoadCsv("dataY.csv");
_dataX = new TFTensor(aX);
_dataY = new TFTensor(aY);
_session = new TFSession();
_graph = _session.Graph;
_input = _graph.Placeholder(TFDataType.Float);
_output = _graph.Placeholder(TFDataType.Float);
_y_out = new LinearLayer(_graph, _input, (int)_dataX.Shape[0], 1);
cost = _graph.ReduceMean(_graph.SigmoidCrossEntropyWithLogits(_y_out.Result, _output));
_gradientDescentOptimizer = new GradientDescentOptimizer(_graph, _cost, _y_out.W, _y_out.b);
_gradientDescentOptimizer.ApplyGradientDescent(_graph);
var runner = _session.GetRunner();
runner.AddTarget(_y_out.InitB.Operation);
runner.Run();
}
public void TrainModelIteration()
{
var runner = _session.GetRunner();
runner.AddInput(_input, _dataX);
runner.AddInput(_output, _dataY);
for (int i = 0; i < 2; i++)
{
runner.Fetch(_gradientDescentOptimizer.Updates[i]);
}
runner.Run();
}
public void Dispose()
{
_graph.Dispose();
_session.Dispose();
}
}
class LinearLayer
{
public TFOutput Result { get; set; }
public TFOutput W { get; set; }
public TFOutput b { get; set; }
public TFOutput InitW { get; set; }
public TFOutput InitB { get; set; }
public LinearLayer(TFGraph graph, TFOutput x, int inSize, int outSize)
{
var wShape = new TFShape(inSize, outSize);
W = graph.VariableV2(wShape, TFDataType.Float);
TFOutput tfOutputWShape = graph.Const(wShape);
TFOutput initialW = graph.RandomUniform(tfOutputWShape, TFDataType.Float);
InitW = graph.Assign(W, initialW);
var bShape = new TFShape(outSize);
b = graph.VariableV2(bShape, TFDataType.Float);
TFOutput tfOutputBShape = graph.Const(bShape);
TFOutput initialB = graph.RandomUniform(tfOutputBShape, TFDataType.Float);
InitB = graph.Assign(b, initialB);
var matMul = graph.MatMul(x, W);
Result = graph.Add(matMul, b);
}
}
class GradientDescentOptimizer
{
private TFOutput[] _variables;
public TFOutput[] Updates { get; set; }
private TFOutput[] _gradients;
public GradientDescentOptimizer(TFGraph graph, TFOutput grad, TFOutput w, TFOutput b)
{
_variables = new TFOutput[4];
_variables[0] = w;
_variables[1] = b;
_gradients = graph.AddGradients(new TFOutput[] { grad }, new TFOutput[] { w, b });
Updates = new TFOutput[4];
}
public void ApplyGradientDescent(TFGraph graph, float alpha = 0.01f)
{
TFOutput tfAlpha = graph.Const(alpha);
for (int i = 0; i < 2; i++)
{
Updates[i] = graph.ApplyGradientDescent(_variables[i], tfAlpha, _gradients[i]);
}
}
}

Related

CSharp Generate polygons from a set of intersecting lines

https://gis.stackexchange.com/questions/58245/generate-polygons-from-a-set-of-intersecting-lines
https://i.stack.imgur.com/UUyHF.png
https://i.stack.imgur.com/3ClRI.png
I want to find the bounding polygon like this link.
I'm using C# nettopologysuite like the jts library shown in this link, but the polygon I want doesn't come out.
How can I extract polygons?
Document doc = Autodesk.AutoCAD.ApplicationServices.Application.DocumentManager.MdiActiveDocument;
Database db = doc.Database;
Editor ed = doc.Editor;
PromptEntityOptions pEntOpt1 = new PromptEntityOptions("Outter Boundary");
pEntOpt1.SetRejectMessage("is Not Polyline");
pEntOpt1.AddAllowedClass(typeof(Polyline), true);
PromptEntityResult pEntRes1 = ed.GetEntity(pEntOpt1);
string typeName = RXObject.GetClass(typeof(Polyline)).DxfName;
TypedValue[] tv = new TypedValue[1] { new TypedValue((int)DxfCode.Start, typeName) };
SelectionFilter sf = new SelectionFilter(tv);
PromptSelectionResult pSelectRes = ed.GetSelection(sf);
if (pSelectRes.Status != PromptStatus.OK)
return;
ObjectId boudaryPlineId = pEntRes1.ObjectId;
ObjectId[] innerPlineids = pSelectRes.Value.GetObjectIds();
ObjectIdCollection innerBoundaries = new ObjectIdCollection(innerPlineids);
try
{
using (doc.LockDocument())
{
using (Transaction tr = db.TransactionManager.StartTransaction())
{
Polygonizer polygonizer = new Polygonizer();
GeometryFactory gf = new GeometryFactory();
Polyline boundaryPline = tr.GetObject(boudaryPlineId, OpenMode.ForWrite) as Polyline;
Coordinate[] coords = new Coordinate[boundaryPline.NumberOfVertices + 1];
for (int j = 0; j < boundaryPline.NumberOfVertices; j++)
{
Point3d pos = boundaryPline.GetPoint3dAt(j);
coords[j] = new Coordinate(pos.X, pos.Y);
}
coords[boundaryPline.NumberOfVertices] = new Coordinate(boundaryPline.StartPoint.X, boundaryPline.StartPoint.Y);
Polygon pg = gf.CreatePolygon(coords);
List<Geometry> plines = new List<Geometry>();
foreach (ObjectId indexid in innerBoundaries)
{
Polyline indexPline = tr.GetObject(indexid, OpenMode.ForWrite) as Polyline;
Coordinate[] coords2 = new Coordinate[indexPline.NumberOfVertices];
for (int i = 0; i < indexPline.NumberOfVertices; i++)
{
Point3d pos = indexPline.GetPoint3dAt(i);
coords2[i] = new Coordinate(pos.X, pos.Y);
}
LineString ls1 = new LineString(coords2);
polygonizer.Add(ls1);
}
ICollection<Geometry> test = polygonizer.GetPolygons();
foreach (Geometry polygon in test)
{
// to do
}
tr.Commit();
}
}
}
catch
{
}
outter Boundary
inner Boundary
The Red Polygon I want to find
This is my second question in English, so please understand the lack of expression.
-------------- After applying the answer below --------------
private List<NetGeometry> GetPolygons(Transaction tr, List<LineString> lineStrings)
{
List<NetGeometry> polygons = new List<NetGeometry>();
Polygonizer polygonizer = new Polygonizer(false);
GeometryFactory gf = new GeometryFactory(new PrecisionModel(10000));
var noder = new SnapRoundingNoder(new PrecisionModel(10000)); // adjust PrecisionModel to your needs, must be fixed.
noder.ComputeNodes(lineStrings.Select(s => (ISegmentString)new NodedSegmentString(s.Coordinates, s)).ToList());
var noded = noder.GetNodedSubstrings();
polygonizer.Add(noded.Select(n => (Geometry)gf.CreateLineString(n.Coordinates)).ToList());
//for ( int i = 0; i < lineStrings.Count; i++)
//{
// polygonizer.Add(lineStrings[i]);
//}
ICollection<NetGeometry> result = polygonizer.GetPolygons();
foreach (NetGeometry index in result)
{
if ( index is Polygon pg)
{
polygons.Add(pg);
}
}
return polygons;
}
Results after application
All input geometries to Polygonizer should be LineStrings.
The whole input set of LineStrings must be fully noded:
var noder = new SnapRoundingNoder(new PrecisionModel(1000)); // adjust PrecisionModel to your needs, must be fixed.
noder.ComputeNodes(lines.Select(s => (ISegmentString)new NodedSegmentString(s.Coordinates, s)).ToList());
var noded = noder.GetNodedSubstrings();
polygonizer.Add(noded.Select(n => (Geometry)gf.CreateLineString(n.Coordinates)).ToList());

Shuffling list of competitors, each month different competitor

for my friends sportscompetition, each player has to play 1 game a month against an other player. Now if i have a list of 20 players or so its not that hard to randomize the first month so i have 10 matches.
All the months after that though i'm not sure how to get the randomizer working so they won't be matched against a player they have played against.
Right now i made an sql database with Players(Name, (int)Id, Email) , Matches(Id, Player1ID, Player2ID)
I'm thinking for a randomize of the list and checking if each match doesn't contain 2 id's from a match in the database. And if 1 match does, redo the entire randomize of that month.
But i'm not sure if thats the best way.
This is what i have so far, i have yet to test it after i add some 'leden' and 'matches' to my database.
private void MaakMatchen(Maand maand)
{
List<Lid> leden = new List<Lid>();
var dbManager = new Manager();
using (var conGildenhof = dbManager.GetConnection())
{
using (var comLeden = conGildenhof.CreateCommand())
{
comLeden.CommandType = CommandType.Text;
comLeden.CommandText = "select * from dbo.Leden";
conGildenhof.Open();
using (var alleleden = comLeden.ExecuteReader())
{
Int32 voornaamPos = alleleden.GetOrdinal("Voornaam");
Int32 familienaamPos = alleleden.GetOrdinal("Familienaam");
Int32 LidNummerPos = alleleden.GetOrdinal("LidNummer");
while (alleleden.Read())
{
leden.Add(new Classes.Lid(alleleden.GetString(voornaamPos), alleleden.GetString(familienaamPos), alleleden.GetInt32(LidNummerPos)));
}
leden = Randomize(leden);
}
}
using (var comInsert = conGildenhof.CreateCommand())
{
comInsert.CommandType = CommandType.Text;
comInsert.CommandText = "Insert into dbo.Matches (Lid1Id, Lid2Id, Maand) values (#lid1, #lid2, #maand)";
var parLid1 = comInsert.CreateParameter();
parLid1.ParameterName = "#lid1";
comInsert.Parameters.Add(parLid1);
var parLid2 = comInsert.CreateParameter();
parLid2.ParameterName = "#lid2";
comInsert.Parameters.Add(parLid2);
var parMaand = comInsert.CreateParameter();
parMaand.ParameterName = "#maand";
comInsert.Parameters.Add(parMaand);
int lengte = leden.Count();
for (int i = 0; i < lengte; i = i + 2)
{
parLid1.Value = leden[i].LidNummer;
parLid2.Value = leden[i + 1].LidNummer;
parMaand.Value = (int)maand;
comInsert.ExecuteNonQuery();
}
}
}
}
private List<Lid> Randomize(List<Lid> leden)
{
for (int i=0;i<100;i++)
{
leden = Shuffle(leden);
}
int temp = CheckUp(leden);
while (temp != 100)
{
leden = Shuffle(leden, temp);
temp = CheckUp(leden);
}
return leden;
}
private List<Lid> Shuffle(List<Lid> leden)
{
Random rnd = new Random();
int a = rnd.Next(1, leden.Count() + 1);
int b = rnd.Next(1, leden.Count() + 1);
var temp = new Lid();
temp = leden[a];
leden[a] = leden[b];
leden[b] = temp;
return leden;
}
private List<Lid> Shuffle(List<Lid> leden, int id)
{
Random rnd = new Random();
int a = rnd.Next(1, leden.Count() + 1);
int b = id;
var temp = new Lid();
temp = leden[a];
leden[a] = leden[b];
leden[b] = temp;
return leden;
}
private int CheckUp(List<Lid> leden)
{
int lengte = leden.Count();
List<Matches> matches = new List<Matches>();
var dbManager = new Manager();
using (var conGildenhof = dbManager.GetConnection())
{
using (var comMatches = conGildenhof.CreateCommand())
{
comMatches.CommandType = CommandType.Text;
comMatches.CommandText = "select * from dbo.Matches";
conGildenhof.Open();
using (var allematches = comMatches.ExecuteReader())
{
Int32 lid1Pos = allematches.GetOrdinal("Lid1Id");
Int32 lid2Pos = allematches.GetOrdinal("Lid2Id");
Int32 maandPos = allematches.GetOrdinal("Maand");
while (allematches.Read())
{
matches.Add(new Classes.Matches(allematches.GetInt32(lid1Pos), allematches.GetInt32(lid2Pos), (Maand)allematches.GetInt32(maandPos)));
}
}
}
}
for (int i=0;i<lengte;i=i+2)
{
foreach (Matches match in matches)
{
if (leden[i].LidNummer == match.Lid1Id)
{
if (leden[i + 1].LidNummer == match.Lid2Id)
return leden[i].LidNummer;
}
if (leden[i].LidNummer == match.Lid2Id)
{
if (leden[i + 1].LidNummer == match.Lid1Id)
return leden[i].LidNummer;
}
}
}
return 100;
}
private void Button_Click(object sender, RoutedEventArgs e)
{
var maand = new int();
int.TryParse(TextBoxMaand.Text, out maand);
if (maand == 0)
TextBoxMaand.Text = "GETAL!";
else
{
MaakMatchen((Maand)maand);
}
}
I would shuffle the players first and create the matches in a second iteration
List<Player> players = GetPlayers();
Random _rnd = new Random();
// shuffle players
players = players.OrderBy(_ => _rnd.Next()).ToList();
// create matches
var matches = players.Take(players.Count / 2).Zip(players.Skip(players.Count / 2), (p1, p2) => new Match(p1,p2));
https://dotnetfiddle.net/sGxbx4

Filter products with ElasticSearch concat a lot of filter

I've been trying to filter products with Elasticsearch for a few hours, unfortunately to no avail.
I need to find products that belong to certain categories and at the same time have selected several brands and one size.
Help :(
json screen
querycontainer build method
private QueryContainer CreateOrQueryFromFilter(QueryContainer queryContainer, SortedSet<string> filter, string fieldName)
{
if (filter != null && filter.Count > 0)
{
foreach (var item in filter)
{
queryContainer |= new TermQuery()
{
Name = fieldName + "named_query",
Boost = 1.1,
Field = fieldName,
Value = item
};
}
}
return queryContainer;
}
and search method
public ResultModel SearchRequest(RequestModel r)
{
string key = string.Format("search-{0}-{1}", r.CacheKey + "-" + ProductCatalog.Model.Extension.StringHelper.UrlFriendly(r.SearchText), r.Prefix);
node = new Uri("http://xxxx:9200/");
settings = new ConnectionSettings(node);
settings.DisableDirectStreaming();
settings.DefaultIndex("products");
client = new ElasticClient(settings);
// return AppCache.Get(key, () =>
// {
DateTime start = DateTime.Now;
ResultModel result = new ResultModel(r.Canonical, r.RouteObject);
if (!string.IsNullOrEmpty(r.Prefix))
{
result.Prefix = r.Prefix;
}
QueryContainer c = new QueryContainer();
if (r.CategoryFilterChilds != null && r.CategoryFilterChilds.Count > 0)
{
var a1 = new SortedSet<string>(r.CategoryFilterChilds.Select(a => (string)a.ToString()));
c = CreateOrQueryFromFilter(c, a1, "categories");
}
else
{
if (r.CategoryFilterRoots != null && r.CategoryFilterRoots.Count > 0)
{
var a1 = new SortedSet<string>(r.CategoryFilterRoots.Select(a => (string)a.ToString()));
c = CreateOrQueryFromFilter(c, a1, "categories");
}
else
{
// null
}
}
var filters = new AggregationDictionary();
if (r.IsBrandFilter)
{
c = CreateOrQueryFromFilter(c, r.SelectedBrands, "brands");
}
if (r.IsColorFilter)
{
c = CreateOrQueryFromFilter(c, r.SelectedBrands, "colors");
}
int skip = (r.Page * r.PageSize) - r.PageSize;
ISearchRequest r2 = new SearchRequest("products");
r2.From = 1;
r2.Size = r.PageSize;
r2.Query = c;
string[] Fields = new[] { "brands", "shopId" };
AggregationBase aggregations = null;
foreach (string sField in Fields)
{
var termsAggregation = new TermsAggregation("agg_" + sField)
{
Field = sField,
Size = 120,
Order = new List<TermsOrder> { TermsOrder.TermDescending }
};
if (aggregations == null)
{
aggregations = termsAggregation;
}
else
{
aggregations &= termsAggregation;
}
}
r2.Aggregations = aggregations;
var c2 = client.Search<ShopProductElastic>(r2);
var ShopsBuf = (Nest.BucketAggregate)(c2.Aggregations["agg_brands"]);
var ShopsCount = ShopsBuf.Items.Count();
var results = c2;
result.BrandsRequest = new SortedDictionary<string, int>();
foreach (Nest.KeyedBucket<object> item in ShopsBuf.Items)
{
result.BrandsRequest.Add((string)item.Key, (int)(item.DocCount ?? 0));
}
result.CategorySelected = r.CategoryCurrent;
result.TotalCount = 10;
var costam = results.Documents.ToList();
var targetInstance = Mapper.Map<List<Products>>(costam);
result.Products = targetInstance;
result.Page = r.Page;
result.PageSize = r.PageSize;
result.IsBrandFilter = r.IsBrandFilter;
result.IsColorFilter = r.IsColorFilter;
result.IsPatternFilter = r.IsPatternFilter;
result.IsSeasonFilter = r.IsSeasonFilter;
result.IsShopFilter = r.IsShopFilter;
result.IsSizeFilter = r.IsSizeFilter;
result.IsStyleFilter = r.IsStyleFilter;
result.IsTextileFilter = r.IsTextileFilter;
DateTime stop = DateTime.Now;
result.SearchTime = stop - start;
result.SearchText = r.SearchText;
return result;
// }, TimeSpan.FromHours(8));
}
I have all products that have a given brand or categories. However, i need all products of a selected brand in selected categories

cannot extract triangulated geometry from IFC using xBIM

I am trying to extract triangulated meshes of all geometries in an IFC file using this code
I have already loaded the model with ifcstore.open...
var context = new Xbim3DModelContext(model);
context.CreateContext();
//var geometries = context.ShapeGeometries();
//XbimShapeTriangulation mesh = null;
var geometries = context.ShapeInstances();
foreach (var g in geometries)
{
//var ms = new MemoryStream(((IXbimShapeGeometryData)g).ShapeData);
//var br = new BinaryReader(ms);
//mesh = br.ReadShapeTriangulation();
////mesh = mesh.Transform(((XbimShapeInstance)g).Transformation);
//Console.WriteLine(g.Format + " | " + g.ShapeLabel);
//Console.WriteLine(mesh.Faces.Count() + " | " + mesh.Vertices.Count());
var tri = context.ShapeGeometryMeshOf(g);
Console.WriteLine(tri.TriangleIndexCount + " | " + tri.ToString());
}
If I'm using the commented part of the above code, the mesh returns without being triangulated. The format is PolyHedronBinary.
If I use the context.ShapeGeometryMeshOf() method, there is an exception thrown : invalid geometry type .
Please help me with triangulating the geometries of the model.
I have also read about the method "read" in XbimWindowsUI/Xbim.Presentation/MeshGeometry3DExtensions.cs, but I am not able to figure out what I have to pass as the "m3d" parameter ?
/// <summary>
/// Reads a triangulated model from an array of bytes and adds the mesh
/// to the current state
/// </summary>
/// <param name="m3D"></param>
/// <param name="mesh">byte array of XbimGeometryType.PolyhedronBinary Data</param>
/// <param name="transform">Transforms the mesh to the new position if not null</param>
public static void Read(
this MeshGeometry3D m3D,
byte[] mesh,
XbimMatrix3D? transform = null)
It will be great if anybody could provide/ point me to example usage of this method.
I need to rebuild the IFC model in Unity and hence I need the triangulated mesh data.
Also suggest if there is anyway to achieve this more efficiently and/or in a simpler way!
I used some code from the xBIM GIT repository for this:
IfcStore model = IfcStore.Open(ifcFileName);
if (model.GeometryStore.IsEmpty)
{
var context = new Xbim3DModelContext(model);
context.CreateContext();
}
foreach (var ifcElement in model.Instances.OfType<IfcElement>())
{
XbimModelPositioningCollection modelPositions = new XbimModelPositioningCollection();
short userDefinedId = 0;
model.UserDefinedId = userDefinedId;
modelPositions.AddModel(model.ReferencingModel);
if (model.IsFederation)
{
foreach (var refModel in model.ReferencedModels)
{
refModel.Model.UserDefinedId = ++userDefinedId;
var v = refModel.Model as IfcStore;
if (v != null)
modelPositions.AddModel(v.ReferencingModel);
}
}
var modelBounds = modelPositions.GetEnvelopeInMeters();
var p = modelBounds.Centroid();
var modelTranslation = new XbimVector3D(-p.X, -p.Y, -p.Z);
var oneMeter = model.ModelFactors.OneMetre;
var translation = XbimMatrix3D.CreateTranslation(modelTranslation * oneMeter);
var scaling = XbimMatrix3D.CreateScale(1 / oneMeter);
var transform = translation * scaling;
var mat = GetStyleFromXbimModel(ifcElement);
var m = GetGeometry(ifcElement, transform, mat);
var myRetTuple = WriteTriangles(m);
}`
the WriteTriangle-Function:
private Tuple<Point3D> WriteTriangles(IXbimMeshGeometry3D wpfMeshGeometry3D)
{
var axesMeshBuilder = new MeshBuilder();
var pos = wpfMeshGeometry3D.Positions.ToArray();
var nor = wpfMeshGeometry3D.Normals.ToArray();
var areasum = 0.00;
for (var i = 0; i < wpfMeshGeometry3D.TriangleIndices.Count; i += 3)
{
var p1 = wpfMeshGeometry3D.TriangleIndices[i];
var p2 = wpfMeshGeometry3D.TriangleIndices[i + 1];
var p3 = wpfMeshGeometry3D.TriangleIndices[i + 2];
if (nor[p1] == nor[p2] && nor[p1] == nor[p3]) // same normals
{
var cnt = FindCentroid(new[] { pos[p1], pos[p2], pos[p3] });
CreateNormal(cnt, nor[p1], axesMeshBuilder);
}
else
{
CreateNormal(pos[p1], nor[p1], axesMeshBuilder);
CreateNormal(pos[p2], nor[p2], axesMeshBuilder);
CreateNormal(pos[p3], nor[p3], axesMeshBuilder);
}
var point1 = new Point3D(pos[p1].X, pos[p1].Y, pos[p1].Z);
var point2 = new Point3D(pos[p2].X, pos[p2].Y, pos[p2].Z);
var point3 = new Point3D(pos[p3].X, pos[p3].Y, pos[p3].Z);
}
return Tuple.Create(point1, point2, point3);
}
and some additional methods from xBIM GeometryHandler:
private static XbimPoint3D FindCentroid(XbimPoint3D[] p)
{
double x = 0;
double y = 0;
double z = 0;
var n = 0;
foreach (var item in p)
{
x += item.X;
y += item.Y;
z += item.Z;
n++;
}
if (n <= 0)
return new XbimPoint3D(x, y, z);
x /= n;
y /= n;
z /= n;
return new XbimPoint3D(x, y, z);
}
private static void CreateNormal(XbimPoint3D pnt, XbimVector3D vector3D, MeshBuilder axesMeshBuilder)
{
var cnt = new Point3D() { X = pnt.X, Y = pnt.Y, Z = pnt.Z };
var path = new List<Point3D> { cnt };
const double nrmRatio = .2;
path.Add(
new Point3D(
cnt.X + vector3D.X * nrmRatio,
cnt.Y + vector3D.Y * nrmRatio,
cnt.Z + vector3D.Z * nrmRatio
));
const double lineThickness = 0.001;
axesMeshBuilder.AddTube(path, lineThickness, 9, false);
}
private static WpfMeshGeometry3D GetGeometry(IPersistEntity selection, XbimMatrix3D modelTransform, Material mat)
{
var tgt = new WpfMeshGeometry3D(mat, mat);
tgt.BeginUpdate();
using (var geomstore = selection.Model.GeometryStore)
{
using (var geomReader = geomstore.BeginRead())
{
foreach (var shapeInstance in geomReader.ShapeInstancesOfEntity(selection).Where(x => x.RepresentationType == XbimGeometryRepresentationType.OpeningsAndAdditionsIncluded))
{
IXbimShapeGeometryData shapegeom = geomReader.ShapeGeometry(shapeInstance.ShapeGeometryLabel);
if (shapegeom.Format != (byte)XbimGeometryType.PolyhedronBinary)
continue;
var transform = shapeInstance.Transformation * modelTransform;
tgt.Add(
shapegeom.ShapeData,
shapeInstance.IfcTypeId,
shapeInstance.IfcProductLabel,
shapeInstance.InstanceLabel,
transform,
(short)selection.Model.UserDefinedId
);
}
}
}
tgt.EndUpdate();
return tgt;
}
private static DiffuseMaterial GetStyleFromXbimModel(IIfcProduct item, double opacity = 1)
{
var context = new Xbim3DModelContext(item.Model);
var productShape = context.ShapeInstancesOf(item)
.Where(s => s.RepresentationType != XbimGeometryRepresentationType.OpeningsAndAdditionsExcluded)
.ToList();
var wpfMaterial = GetWpfMaterial(item.Model, productShape.Count > 0 ? productShape[0].StyleLabel : 0);
var newmaterial = wpfMaterial.Clone();
((DiffuseMaterial)newmaterial).Brush.Opacity = opacity;
return newmaterial as DiffuseMaterial;
}
private static Material GetWpfMaterial(IModel model, int styleId)
{
var sStyle = model.Instances[styleId] as IIfcSurfaceStyle;
var wpfMaterial = new WpfMaterial();
if (sStyle != null)
{
var texture = XbimTexture.Create(sStyle);
texture.DefinedObjectId = styleId;
wpfMaterial.CreateMaterial(texture);
return wpfMaterial;
}
var defautMaterial = ModelDataProvider.DefaultMaterials;
Material material;
if (defautMaterial.TryGetValue(model.GetType().Name, out material))
{
return material;
}
var color = new XbimColour("red", 1, 1, 1);
wpfMaterial.CreateMaterial(color);
return wpfMaterial;
}

Group by using Linq to get unique records from list

I have below code for a list, in which I can have duplicate records for list.
At the final result I need to remove duplicate entries and make them unique. I have written the code as below,
public class BusLocation
{
public string Latitude { get; set; }
public string Longitude { get; set; }
public string BusNumber { get; set; }
public Int64 ScheduleId { get; set; }
public Int64 TripId { get; set; }
}
List<BusLocation> _BusLocation=new List<BusLocation>();
BusLocation loc = new BusLocation();
loc.BusNumber = "123";
loc.Latitude = "12.1234";
loc.Longitude = "44.1234";
loc.ScheduleId = 77;
loc.TripId = 1678;
_BusLocation.Add(loc);
loc.BusNumber = "123";
loc.Latitude = "12.1234";
loc.Longitude = "44.1234";
loc.ScheduleId = 77;
loc.TripId = 1678;
_BusLocation.Add(loc);
loc.BusNumber = "123";
loc.Latitude = "12.1334";
loc.Longitude = "44.1334";
loc.ScheduleId = 78;
loc.TripId = 1679;
_BusLocation.Add(loc);
var result1 = _BusLocation
.GroupBy(lat => lat.Latitude)
.Select(grp => grp.ToList())
.ToList();
But its gives me wrong result as the last records are repeated 3 times,
I want the out put in the list as
loc.BusNumber = "123";
loc.Latitude = "12.1234";
loc.Longitude = "44.1234";
loc.ScheduleId = 77;
loc.TripId = 1678;
loc.BusNumber = "123";
loc.Latitude = "12.1334";
loc.Longitude = "44.1334";
loc.ScheduleId = 78;
loc.TripId = 1679;
Any suggestions are most welcome.
Your code has a bug. Instead of adding three distinct BusLocation instances to the list you add the same instance three times. You can fix your code and make it more terse by using initializers:
List<BusLocation> _BusLocation = new List<BusLocation>() {
new BusLocation {
BusNumber = "123",
Latitude = "12.1234",
Longitude = "44.1234",
ScheduleId = 77,
TripId = 1678
},
new BusLocation {
Latitude = "12.1234",
Longitude = "44.1234",
ScheduleId = 77,
TripId = 1678
},
new BusLocation {
BusNumber = "123",
Latitude = "12.1334",
Longitude = "44.1334",
ScheduleId = 78,
TripId = 1679
}
};
To remove duplicates you need to define what is a duplicate. You can do that by creating an IEqualityComparer<BusLocation> class:
class BusLocationEqualityComparer : IEqualityComparer<BusLocation> {
public Boolean Equals(BusLocation x, BusLocation y) {
return x.BusNumber == y.BusNumber
&& x.Latitude == y.Latitude
&& x.Longitude == y.Longitude;
}
public Int32 GetHashCode(BusLocation obj) {
unchecked {
const Int32 Multiplier = -1521134295;
var hash = -1901080290;
hash = hash*Multiplier + obj.Latitude?.GetHashCode() ?? 0;
hash = hash*Multiplier + obj.Longitude?.GetHashCode() ?? 0;
hash = hash*Multiplier + obj.BusNumber?.GetHashCode() ?? 0;
return hash;
}
}
}
Here equality is defined as having the same bus number, latitude and longitude. However, as you are using strings for latitude and longitude I am not sure that will work but I will leave that to you.
To get the distinct bus locations you can then use LINQ:
var distinctBusLocations = _BusLocation.Distinct(new BusLocationEqualityComparer());
I would suggest you to use Distinct() function.
https://msdn.microsoft.com/library/bb348436(v=vs.100).aspx
It would look similar like this:
var result1 = _BusLocation.GroupBy(lat => lat.Latitude).Select(grp => grp.ToList()).ToList().Distinct();

Categories