コード例 #1
0
        [Route("/[controller]/AreaPlaceData/{code}/{styleSet}/{dataKey}")] //Draw an area using place data.
        public ActionResult DrawPlusCodeCustomElements(string code, string styleSet, string dataKey)
        {
            try
            {
                PerformanceTracker pt = new PerformanceTracker("DrawTilePlace");
                MapTileSupport.GetPlusCodeImagePixelSize(code, out var imgX, out var imgY);
                var info = new ImageStats(OpenLocationCode.DecodeValid(code), imgX, imgY);

                if (!DataCheck.IsInBounds(cache.Get <IPreparedGeometry>("serverBounds"), info.area))
                {
                    pt.Stop("OOB");
                    return(StatusCode(500));
                }

                byte[] tileData = getExistingSlippyTile(code, styleSet);
                if (tileData != null)
                {
                    pt.Stop(code + "|" + styleSet);
                    return(File(tileData, "image/png"));
                }

                //Make tile
                var places   = GetPlacesForTile(info, null, styleSet, false);
                var paintOps = MapTileSupport.GetPaintOpsForCustomDataElements(dataKey, styleSet, info);
                tileData = FinishMapTile(info, paintOps, code, styleSet);

                pt.Stop(code + "|" + styleSet + "|" + Configuration.GetValue <string>("MapTilesEngine"));
                return(File(tileData, "image/png"));
            }
            catch (Exception ex)
            {
                ErrorLogger.LogError(ex);
                return(StatusCode(500));
            }
        }
コード例 #2
0
        /// <summary>
        /// Create the map tiles for an offline game, to be stored on the device alongside the database.
        /// </summary>
        /// <param name="relationID">the OSM relation the game will be built around</param>
        /// <param name="buffered">the GeoArea to use as the gameplay area</param>
        /// <param name="allPlaces">List of OSM Elements to use while drawing tiles</param>
        /// <param name="saveToFolder">If true, save images to a named folder. Does not currently save any output if this is false.</param>
        public static void DrawMapTilesStandalone(long relationID, GeoArea buffered, List <DbTables.Place> allPlaces, bool saveToFolder)
        {
            var intersectCheck = Converters.GeoAreaToPolygon(buffered);
            //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     = buffered.LongitudeWidth / resolutionCell8;
            var yTiles     = buffered.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.
            //This is tricky to run in parallel because it's not smooth increments
            var yCoords = new List <double>();
            var yVal    = swCorner.Decode().SouthLatitude;

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

            var xCoords = new List <double>();
            var xVal    = swCorner.Decode().WestLongitude;

            while (xVal <= neCorner.Decode().EastLongitude)
            {
                xCoords.Add(xVal);
                xVal += resolutionCell8;
            }
            System.Threading.ReaderWriterLockSlim dbLock = new System.Threading.ReaderWriterLockSlim();


            foreach (var y in yCoords)
            {
                //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     row     = Converters.GeoAreaToPolygon(thisRow);
                var     rowList = allPlaces.Where(a => row.Intersects(a.ElementGeometry)).ToList();

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

                    var areaForTile = new GeoArea(new GeoPoint(plusCodeArea.SouthLatitude, plusCodeArea.WestLongitude), new GeoPoint(plusCodeArea.NorthLatitude, plusCodeArea.EastLongitude));
                    var acheck      = Converters.GeoAreaToPolygon(areaForTile);                          //this is faster than using a PreparedPolygon in testing, which was unexpected.
                    var areaList    = rowList.Where(a => acheck.Intersects(a.ElementGeometry)).ToList(); //This one is for the maptile

                    //Create the maptile first, so if we save it to the DB/a file we can call the lock once per loop.
                    int imgX = 0, imgY = 0;
                    MapTileSupport.GetPlusCodeImagePixelSize(plusCode8, out imgX, out imgY);
                    var info    = new ImageStats(areaForTile, imgX, imgY); //Each pixel is a Cell11, we're drawing a Cell8. For Cell6 testing this is 1600x2000, just barely within android limits
                    byte[] tile = null;                                    // MapTiles.DrawAreaAtSize(info, areaList);
                    if (tile == null)
                    {
                        Log.WriteLog("Tile at " + x + "," + y + "Failed to draw!");
                        return;
                    }
                    if (saveToFolder) //some apps, like my Solar2D apps, can't use the byte[] in a DB row and need files.
                    {
                        //This split helps (but does not alleviate) Solar2D performance.
                        //A county-sized app will function this way, though sometimes when zoomed out it will not load all map tiles on an android device.
                        Directory.CreateDirectory(relationID + "Tiles\\" + plusCode8.Substring(0, 6));
                        System.IO.File.WriteAllBytes(relationID + "Tiles\\" + plusCode8.Substring(0, 6) + "\\" + plusCode8.Substring(6, 2) + ".pngTile", tile); //Solar2d also can't load pngs directly from an apk file in android, but the rule is extension based.
                    }

                    mapTileCounter++;
                    //if (progressTimer.ElapsedMilliseconds > 15000)
                    //{

                    //    progressTimer.Restart();
                    //}
                });
                Log.WriteLog(mapTileCounter + " tiles processed, " + Math.Round((mapTileCounter / totalTiles) * 100, 2) + "% complete");
            }//);
        }