Newer
Older
using System;
using System.Collections.Generic;
using System.IO;
using System.Threading.Tasks;
using UnityEngine;
// Always multiple of 3. Each 3 form a triangle.
public struct TriangulatedPolygon
public List<Vector2> vertices;
public bool Valid => valid;
public bool Finished => finished;
public bool Triangulated => triangulated;
private bool valid;
private bool finished;
private bool triangulated;
private Task parseTileTask;
private List<VectorTileLayer> layers;
private List<List<TriangulatedPolygon>> polygonLayers;
internal UnityWebRequest request;
public void StartDownload()
{
Debug.Assert(!started);
request.SendWebRequest();
started = true;
}
public bool IsFinished()
{
if (finished)
return true;
if (!request.isDone)
return false;
layers = VectorTileParser.Parse( stream );
} );
}
else if (parseTileTask.IsCompleted)
{
valid = parseTileTask.IsCompletedSuccessfully;
finished = true;
parseTileTask = null;
}
}
return finished;
}
// Number of failed polys to triangulate are returned.
public int Triangulate()
if (triangulated )
return 0;
int numFailedPolys = 0;
polygonLayers = new List<List<TriangulatedPolygon>>();
List<double> vertices = new List<double>();
List<int> holeIndices = new List<int>();
for ( int i =0; i < layers.Count; i++ )
{
var features = layers[i].VectorTileFeatures;
var polygons = new List<TriangulatedPolygon>();
for (int j = 0; j< features.Count; j++)
{
if (features[j].GeometryType != Tile.GeomType.Polygon)
continue;
vertices.Clear();
holeIndices.Clear();
var rings = features[j].Geometry;
for (int k = 0;k < rings.Count;k++)
{
if (k != 0) // if is not first ring, this is a hole
{
holeIndices.Add( vertices.Count / 2 );
}
var ring = rings[k];
for (int q = 0;q < ring.Count;q++)
{
vertices.Add( ring[q].X );
vertices.Add( ring[q].Y );
}
}
try
{
var indices = EarcutNet.Earcut.Tessellate( vertices, holeIndices );
TriangulatedPolygon poly = new TriangulatedPolygon();
poly.vertices = new List<Vector2>( indices.Count );
{
double x = vertices[indices[k]*2];
double y = vertices[indices[k]*2+1];
poly.vertices.Add( new Vector2( (float)x, (float)y ) );
}
polygons.Add( poly );
Debug.Assert( poly.vertices.Count % 3 == 0 );
}
catch (Exception)
{
numFailedPolys++;
}
}
polygonLayers.Add( polygons );
}
triangulated = true;
return numFailedPolys;
}
// Calls callback(x, y, channel) for each raster position (256 channels) where each value represents a single channel.
// If no polygon was hit, 255 is called.
// If polygon was hit, but no feature was matched, 254 is called.
public void RenderToTextureSingle( int width, int height, string attributeKeyMatch, List<List<string>> layerNamesList, Func<int, int, byte, bool> callback )
{
Debug.Assert( triangulated, "First call Triangulate." );
Debug.Assert( layerNamesList.Count < 254, "254 and 255 are reserved for no hit or no match." );
bool cancel = false;
// First match layers to layer names.
for (int l = 0;l < layers.Count && !cancel;l++)
var layer = layers[l];
for (int f = 0;f < layer.VectorTileFeatures.Count && !cancel;f++)
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
var feature = layer.VectorTileFeatures[f];
feature.SelectedLayerIdx = 254;
if (feature.GeometryType != Tile.GeomType.Polygon)
continue;
if (feature.Attributes == null)
continue;
// Find matching layer.
bool matchingLayerFound = false;
for (int a = 0;a < feature.Attributes.Count && !matchingLayerFound;a++)
{
if (feature.Attributes[a].Key != attributeKeyMatch)
continue;
string function = feature.Attributes[a].Value as string;
for (int layerIdx = 0;layerIdx < layerNamesList.Count && !matchingLayerFound;layerIdx++)
{
for (int layerNameIdx = 0;layerNameIdx < layerNamesList[layerIdx].Count;layerNameIdx++)
{
if (function.Contains( layerNamesList[layerIdx][layerNameIdx] ))
{
feature.SelectedLayerIdx = layerIdx;
matchingLayerFound = true;
break;
}
}
}
}
}
}
// For each layer, for each pixel, now check triangle intersections.
for (int l = 0;l < layers.Count && !cancel;l++)
{
var layer = layers[l];
for (int y = 0;y < height && !cancel;y++)
{
for (int x = 0;x < width && !cancel;x++)
Vector2 p = new Vector2(x, y);
for (int f = 0;f < layer.VectorTileFeatures.Count && !cancel;f++)
{
var feature = layer.VectorTileFeatures[f];
continue;
var triangles = polygonLayers[l][f].vertices;
bool hit = false;
{
hit = GeomUtil.PointIsInsideTriangle( p, triangles[i], triangles[i+1], triangles[i+2] );
cancel = callback(x, y, (byte)feature.SelectedLayerIdx); // If SelectedLayerIdx did not match, it was set to 254.
break;
}
}
}
}
}
public static VectorTile LoadFromUrl( string url, bool autoStart=true )
tile.request = UnityWebRequest.Get( url );
if (autoStart)
{
tile.StartDownload();
}