Exemplo n.º 1
0
        //Places will be the name for interactible or important areas on the map. Generally, a Place in the DB.
        //(vs Area, which is a PlusCode of any size)

        //All elements in the table with Geometry will be valid, and the TagParser rules will determine which ones are game elements
        //this allows it to be customized much easier, and changed on the fly without reloading data.

        /// <summary>
        /// The core for pulling in locations from PraxisMapper. Can do a new search on an existing list of Place or pulls from the database if none is provided. Adds padding as set from config automatically.
        /// </summary>
        /// <param name="area">The GeoArea to intersect locations against, and include ones that do. </param>
        /// <param name="source">Null to load from the database, or a List of Places to narrow down</param>
        /// <param name="filterSize">Removes any areas with a length or perimeter over this value. Defaults to 0 to include everything.</param>
        /// <param name="styleSet">A style set to run the found locations through for identification.</param>
        /// <param name="skipTags">If true, skips over tagging elements. A performance boost when you have a List to narrow down already.</param>
        /// <param name="includePoints">If false, removes Points from the source before returning the results</param>
        /// <returns>A list of Places that intersect the area, have a perimter greater than or equal to filtersize.</returns>
        public static List <DbTables.Place> GetPlaces(GeoArea area, List <DbTables.Place> source = null, double filterSize = 0, string styleSet = "mapTiles", bool skipTags = false, bool includePoints = true)
        {
            //parameters i will need to restore later.
            //bool includeGenerated = false;

            //The flexible core of the lookup functions. Takes an area, returns results that intersect from Source. If source is null, looks into the DB.
            //Intersects is the only indexable function on a geography column I would want here. Distance and Equals can also use the index, but I don't need those in this app.
            List <DbTables.Place> places;

            if (source == null)
            {
                var paddedArea = GeometrySupport.MakeBufferedGeoArea(area);
                var location   = Converters.GeoAreaToPolygon(paddedArea); //Prepared items don't work on a DB lookup.
                var db         = new PraxisContext();
                db.Database.SetCommandTimeout(new TimeSpan(0, 5, 0));
                if (skipTags) //Should make the load slightly faster if we're parsing existing items that already got tags applied
                {
                    places = db.Places.Where(md => location.Intersects(md.ElementGeometry) && md.AreaSize >= filterSize && (includePoints || md.SourceItemType != 1)).OrderByDescending(w => w.ElementGeometry.Area).ThenByDescending(w => w.ElementGeometry.Length).ToList();
                    return(places); //Jump out before we do ApplyTags
                }
                else
                {
                    places = db.Places.Include(s => s.Tags).Where(md => location.Intersects(md.ElementGeometry) && md.AreaSize >= filterSize && (includePoints || md.SourceItemType != 1)).OrderByDescending(w => w.ElementGeometry.Area).ThenByDescending(w => w.ElementGeometry.Length).ToList();
                }
            }
            else
            {
                var location = Converters.GeoAreaToPreparedPolygon(area);
                places = source.Where(md => location.Intersects(md.ElementGeometry) && md.AreaSize >= filterSize && (includePoints || md.SourceItemType != 1)).Select(md => md.Clone()).ToList();
            }

            if (!skipTags)
            {
                TagParser.ApplyTags(places, styleSet); //populates the fields we don't save to the DB.
            }
            return(places);
        }
Exemplo n.º 2
0
        /// <summary>
        /// Creates all gameplay tiles for a given area and saves them to the database (or files, if that option is set)
        /// </summary>
        /// <param name="areaToDraw">the GeoArea of the area to create tiles for.</param>
        /// <param name="saveToFiles">If true, writes to files in the output folder. If false, saves to DB.</param>
        public static void PregenMapTilesForArea(GeoArea areaToDraw, bool saveToFiles = false)
        {
            //There is a very similar function for this in Standalone.cs, but this one writes back to the main DB.
            var intersectCheck = Converters.GeoAreaToPolygon(areaToDraw);
            //start drawing maptiles and sorting out data.
            var swCorner = new OpenLocationCode(intersectCheck.EnvelopeInternal.MinY, intersectCheck.EnvelopeInternal.MinX);
            var neCorner = new OpenLocationCode(intersectCheck.EnvelopeInternal.MaxY, intersectCheck.EnvelopeInternal.MaxX);

            //declare how many map tiles will be drawn
            var xTiles     = areaToDraw.LongitudeWidth / resolutionCell8;
            var yTiles     = areaToDraw.LatitudeHeight / resolutionCell8;
            var totalTiles = Math.Truncate(xTiles * yTiles);

            Log.WriteLog("Starting processing maptiles for " + totalTiles + " Cell8 areas.");
            long mapTileCounter = 0;

            System.Diagnostics.Stopwatch progressTimer = new System.Diagnostics.Stopwatch();
            progressTimer.Start();

            //now, for every Cell8 involved, draw and name it.
            var yCoords = new List <double>((int)(intersectCheck.EnvelopeInternal.Height / resolutionCell8) + 1);
            var yVal    = swCorner.Decode().SouthLatitude;

            while (yVal <= neCorner.Decode().NorthLatitude)
            {
                yCoords.Add(yVal);
                yVal += resolutionCell8;
            }

            var xCoords = new List <double>((int)(intersectCheck.EnvelopeInternal.Width / resolutionCell8) + 1);
            var xVal    = swCorner.Decode().WestLongitude;

            while (xVal <= neCorner.Decode().EastLongitude)
            {
                xCoords.Add(xVal);
                xVal += resolutionCell8;
            }

            var allPlaces = GetPlaces(areaToDraw);

            object   listLock   = new object();
            DateTime expiration = DateTime.Now.AddYears(10);

            foreach (var y in yCoords)
            {
                System.Diagnostics.Stopwatch thisRowSW = new System.Diagnostics.Stopwatch();
                thisRowSW.Start();
                var db = new PraxisContext();
                db.ChangeTracker.AutoDetectChangesEnabled = false;
                //Make a collision box for just this row of Cell8s, and send the loop below just the list of things that might be relevant.
                //Add a Cell8 buffer space so all elements are loaded and drawn without needing to loop through the entire area.
                GeoArea thisRow     = new GeoArea(y - ConstantValues.resolutionCell8, xCoords.First() - ConstantValues.resolutionCell8, y + ConstantValues.resolutionCell8 + ConstantValues.resolutionCell8, xCoords.Last() + resolutionCell8);
                var     rowList     = GetPlaces(thisRow, allPlaces);
                var     tilesToSave = new List <MapTile>(xCoords.Count());

                Parallel.ForEach(xCoords, x =>
                {
                    //make map tile.
                    var plusCode     = new OpenLocationCode(y, x, 10);
                    var plusCode8    = plusCode.CodeDigits.Substring(0, 8);
                    var plusCodeArea = OpenLocationCode.DecodeValid(plusCode8);
                    var paddedArea   = GeometrySupport.MakeBufferedGeoArea(plusCodeArea);

                    var acheck   = Converters.GeoAreaToPreparedPolygon(paddedArea);                   //Fastest search option is one preparedPolygon against a list of normal geometry.
                    var areaList = rowList.Where(a => acheck.Intersects(a.ElementGeometry)).ToList(); //Get the list of areas in this maptile.

                    int imgX = 0, imgY = 0;
                    GetPlusCodeImagePixelSize(plusCode8, out imgX, out imgY);
                    var info = new ImageStats(plusCodeArea, imgX, imgY);
                    //new setup.
                    var areaPaintOps = GetPaintOpsForStoredElements(areaList, "mapTiles", info);
                    var tile         = DrawPlusCode(plusCode8, areaPaintOps, "mapTiles");

                    if (saveToFiles)
                    {
                        File.WriteAllBytes("GameTiles\\" + plusCode8 + ".png", tile);
                    }
                    else
                    {
                        var thisTile = new MapTile()
                        {
                            TileData = tile, PlusCode = plusCode8, ExpireOn = expiration, AreaCovered = acheck.Geometry, StyleSet = "mapTiles"
                        };
                        lock (listLock)
                            tilesToSave.Add(thisTile);
                    }
                });
                mapTileCounter += xCoords.Count();
                if (!saveToFiles)
                {
                    db.MapTiles.AddRange(tilesToSave);
                    db.SaveChanges();
                }
                Log.WriteLog(mapTileCounter + " tiles processed, " + Math.Round((mapTileCounter / totalTiles) * 100, 2) + "% complete, " + Math.Round(xCoords.Count() / thisRowSW.Elapsed.TotalSeconds, 2) + " tiles per second.");
            }//);
            progressTimer.Stop();
            Log.WriteLog("Area map tiles drawn in " + progressTimer.Elapsed.ToString() + ", averaged " + Math.Round(mapTileCounter / progressTimer.Elapsed.TotalSeconds, 2) + " tiles per second.");
        }