/// <summary> /// Creates a Bitmap image with size width,height. /// From the area (x1,y2) to (x2,y2) where x1,y1,x2,y2 are /// geological coordinates. /// </summary> public Bitmap GetTile(double x1, double y1, double x2, double y2, int width, int height) { Bitmap tile = new Bitmap(width, height); int zoomLevel = GetZoomLevel(x1, x2, width); BBox box = new BBox(x1, y1, x2, y2); // Make searchbox wider dependent on zoomlevel. // Explanation at summary of getSearchBBox. BBox searchBox = getSearchBBox(box, zoomLevel); // Clears image with grey backgroundcolor. Graphics.FromImage(tile).Clear(Color.FromArgb(230, 230, 230)); // Draww all seperate elements in following order: // Land -> Buildings -> Streets -> extra locations (Busstations, parking signs) drawLandCurves(box, tile, graph.GetLandsInBBox(searchBox)); drawBuildingCurves(box, tile, graph.GetBuildingsInBBox(searchBox)); drawStreetCurves(box, tile, graph.GetWaysInBBox(searchBox), zoomLevel); drawExtras(box, tile, graph.GetExtrasInBBox(BBox.getResizedBBox(box, 2)), zoomLevel); //drawAdditionalCurves(box, tile, graph.GetExtrasinBBOX(searchBox), zoomLevel); //used for debugging //Graphics.FromImage(tile).DrawLines(Pens.LightGray, new Point[] { Point.Empty, new Point(0, height), new Point(width, height), new Point(width, 0), Point.Empty }); return tile; }
/// <summary> /// Resizes a BBox by a factor so that the middle stays /// at the same location. /// </summary> public static BBox getResizedBBox(BBox box, double factor) { BBox resizedBBox = new BBox(box.XMin, box.YMin, box.XMax, box.YMax); resizedBBox.XMin -= (factor - 1) * box.Width / 2; resizedBBox.XMax += (factor - 1) * box.Width / 2; resizedBBox.YMin -= (factor - 1) * box.Height / 2; resizedBBox.YMax += (factor - 1) * box.Height / 2; return(resizedBBox); }
/// <summary> /// Draws all buildings in "buildingCurves" on Bitmap tile with box /// containing the start and end point of the tile in geological coordinates. /// </summary> private void drawBuildingCurves(BBox box, Bitmap tile, Curve[] buildingCurves) { foreach (Curve buildingCurve in buildingCurves) { Brush brush = getBrushFromCurveType(buildingCurve.Type); if (brush != null) { drawLanduse(box, tile, buildingCurve, brush); } } }
/// <summary> /// Draws all landpieces in "landCurves" on Bitmap tile with box /// containing the start and end point of the tile in geological coordinates. /// </summary> private void drawLandCurves(BBox box, Bitmap tile, Curve[] landCurves) { foreach (Curve landCurve in landCurves) { Brush brush = getBrushFromCurveType(landCurve.Type); if (brush != null) { drawLanduse(box, tile, landCurve, brush); } } }
/// <summary> /// Draws a location on the map with Image "icon" on /// the Bitmap "tile", where box represents the position /// and size of the tile in geological coordinates. /// </summary> private void drawExtra(BBox box, Bitmap tile, Location location, Image icon) { Graphics gr = Graphics.FromImage(tile); gr.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias; Point start = nodeToTilePoint(box, tile, new Node(box.XMin, box.YMax, 0)); Point p = nodeToTilePoint(box, tile, location); gr.DrawImage(icon, new Point(p.X - start.X, -p.Y + start.Y)); }
/// <summary> /// Draws all streets in "streetCurves" on Bitmap tile with box /// containing the start and end point of the tile in geological coordinates. /// Streets will only be drawn in certain zoomlevels. /// </summary> private void drawStreetCurves(BBox box, Bitmap tile, Curve[] streetCurves, int zoomLevel) { foreach (Curve streetCurve in streetCurves) { Pen pen = getPenFromCurveType(streetCurve.Type, zoomLevel); if (pen != null) { drawStreet(box, tile, streetCurve, getPenFromCurveType(streetCurve.Type, zoomLevel)); } } }
/// <summary> /// Draws all locations in "extraLocations" on Bitmap tile with box /// containing the start and end point of the tile in geological coordinates. /// Locations will only be drawn in certain zoomlevels. /// </summary> private void drawExtras(BBox box, Bitmap tile, Location[] extraLocations, int zoomLevel) { foreach (Location extraLocation in extraLocations) { Image icon = getIconFromLocationType(extraLocation.Type, zoomLevel); if (icon != null) { drawExtra(box, tile, extraLocation, icon); } } }
/// <summary> /// Resizes the BBox box if zoomlevel is very low. /// This is because when curves pass through a tile but no nodes /// of that curve are in that tile the curve won't be drawn in that tile. /// So on very low zoomlevels, we need to search wider to draw all /// curves in a tile. /// </summary> private BBox getSearchBBox(BBox box, int zoomLevel) { if (zoomLevel == 1) { return(BBox.getResizedBBox(box, 3)); } if (zoomLevel == 0) { return(BBox.getResizedBBox(box, 7)); } return(box); }
/// <summary> /// Returns true if the tileCorner with index 'id' is in the screen. /// </summary> private bool IsInScreen(int id) { if (id >= tileCorners[tileIndex].Count) { return(false); } Coordinate c1 = PointToCoord(tileCorners[tileIndex][id].X, tileCorners[tileIndex][id].Y); Coordinate c2 = PointToCoord(tileCorners[tileIndex][id].X + bmpWidth, tileCorners[tileIndex][id].Y + bmpHeight); BBox box = new BBox(c1.Longitude, c1.Latitude, c2.Longitude, c2.Latitude); return(this.bounds.IntersectWith(box)); }
/// <summary> /// Draws a piece of land or building using Brush "brush" /// on the Bitmap "tile", where box represents the position /// and size of the tile in geological coordinates. /// </summary> private void drawLanduse(BBox box, Bitmap tile, Curve curve, Brush brush) { List <Point> polygonPoints = new List <Point>(); Point start = nodeToTilePoint(box, tile, new Node(box.XMin, box.YMax, 0)); Graphics gr = Graphics.FromImage(tile); gr.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias; for (int i = 0; i < curve.AmountOfNodes; i++) { Node node = graph.GetNode(curve[i]); if (node.Longitude != 0 && node.Latitude != 0) { Point p = nodeToTilePoint(box, tile, node); polygonPoints.Add(new Point(p.X - start.X, -p.Y + start.Y)); } } gr.FillPolygon(brush, polygonPoints.ToArray()); }
public Curve[] GetCurvesInBBox(BBox box) { List<Curve> list = new List<Curve>(); list.AddRange(GetLandsInBBox(box)); list.AddRange(GetBuildingsInBBox(box)); list.AddRange(GetWaysInBBox(box)); return list.ToArray(); }
/// <summary> /// Control that draws the map and updates the tiles. /// </summary> public MapDisplay(int x, int y, int width, int height, LoadingThread thr) { this.Location = new Point(x, y); this.Width = width; this.Height = height; this.bounds = new BBox(5.16130, 52.06070, 5.19430, 52.09410); this.DoubleBuffered = true; this.updateStatusDelegate = new UpdateStatusDelegate(UpdateStatus); this.updateRouteStatsDelegate = new UpdateRouteStatsDelegate(UpdateRouteStats); this.startLogoDelegate = new StartLogoDelegate(StartLoadingLogo); this.stopLogoDelegate = new StopLogoDelegate(StopLoadingLogo); this.updateThread = new Thread(new ThreadStart(this.UpdateTiles)); this.MouseClick += (object o, MouseEventArgs mea) => { OnClick(o, new MouseMapDragEventArgs(null, mea.Button, mea.Clicks, mea.X, mea.Y, mea.Delta)); }; this.MouseDoubleClick += OnDoubleClick; this.Paint += OnPaint; this.Resize += OnResize; this.MouseDown += OnMouseDown; this.MouseUp += OnMouseUp; this.MouseMove += OnMouseMove; this.Disposed += (object o, EventArgs ea) => { updateThread.Abort(); }; // Thread that loads the graph. loadingThread = thr; // Checks whether the graph is loaded so the mapdisplay can start loading tiles. loadingTimer = new System.Windows.Forms.Timer(); loadingTimer.Interval = 100; loadingTimer.Tick += (object o, EventArgs ea) => { DoUpdate(); }; loadingTimer.Start(); logo = new AllstarsLogo(true); logo.Location = Point.Empty; logo.Width = this.Width; logo.Height = this.Height; this.Controls.Add(logo); logo.Start(); LinkLabel creditLabel = new LinkLabel(); creditLabel.Text = "© OpenStreetMap contributors"; creditLabel.LinkArea = new LinkArea(2, 13); creditLabel.LinkClicked += (object o, LinkLabelLinkClickedEventArgs ea) => { System.Diagnostics.Process.Start("http://www.openstreetmap.org/copyright/en"); }; creditLabel.Anchor = (AnchorStyles.Right | AnchorStyles.Bottom); creditLabel.Size = creditLabel.PreferredSize; creditLabel.Location = new Point(this.Width - creditLabel.Width - 1, this.Height - creditLabel.Height - 1); this.Controls.Add(creditLabel); statLabel = new Label(); statLabel.AutoSize = true; statLabel.Resize += (object o, EventArgs ea) => { statLabel.Location = new Point(this.Width - 1 - statLabel.Size.Width, 1); }; statLabel.Anchor = (AnchorStyles.Right | AnchorStyles.Top); statLabel.Font = new Font("Microsoft Sans Serif", 11); this.Controls.Add(statLabel); myVehicles = new List<MyVehicle>(); icons = new List<MapIcon>(); streetSelection = new List<Curve>(); tiles = new List<List<Bitmap>>(); tileCorners = new List<List<Point>>(); tiles.Add(new List<Bitmap>()); tileCorners.Add(new List<Point>()); tileIndex = 0; tileIndexes = new List<SortedList<int, SortedList<int, int>>>(); tileIndexes.Add(new SortedList<int, SortedList<int, int>>()); zoomWidth = new List<double>(); zoomHeight = new List<double>(); this.Disposed += (sender, e) => { logo.StillLoading = false; }; // Set linecaps for the pens. footPen.SetLineCap(LineCap.Round, LineCap.Round, DashCap.Round); bikePen.SetLineCap(LineCap.Round, LineCap.Round, DashCap.Round); carPen.SetLineCap(LineCap.Round, LineCap.Round, DashCap.Round); busPen.SetLineCap(LineCap.Round, LineCap.Round, DashCap.Round); otherPen.SetLineCap(LineCap.Round, LineCap.Round, DashCap.Round); }
/// <summary> /// Resizes a BBox by a factor so that the middle stays /// at the same location. /// </summary> public static BBox getResizedBBox(BBox box, double factor) { BBox resizedBBox = new BBox(box.XMin, box.YMin, box.XMax, box.YMax); resizedBBox.XMin -= (factor - 1) * box.Width / 2; resizedBBox.XMax += (factor - 1) * box.Width / 2; resizedBBox.YMin -= (factor - 1) * box.Height / 2; resizedBBox.YMax += (factor - 1) * box.Height / 2; return resizedBBox; }
public Graph(string path) { List<long>[,] geoBlocks = null; datasource = path; // Buffer is 1024 fileblocks big FileStream file = new FileStream(datasource, FileMode.Open, FileAccess.Read, FileShare.Read, 8 * 1024 * 1024); // List of file-positions where blocks with ways and/or // relations start. List<long> wayBlocks = new List<long>(); // We will read the fileblocks in parallel List<long> blocks = new List<long>(); List<long> busNodes = new List<long>(); List<string> busNames = new List<string>(); Console.WriteLine("Finding blocks"); while (true) { long blockstart = file.Position; BlobHeader blobHead = readBlobHeader(file); if (blobHead == null) break; if (blobHead.Type == "OSMHeader") { HeaderBlock filehead = HeaderBlock.ParseFrom( readBlockData(file, blobHead.Datasize)); for (int i = 0; i < filehead.RequiredFeaturesCount; i++) { string s = filehead.GetRequiredFeatures(i); if (s != "DenseNodes" && s != "OsmSchema-V0.6") { throw new NotSupportedException(s); } } // The .000000001 is 'cause longs are stored fileBounds = new BBox(.000000001 * filehead.Bbox.Left, .000000001 * filehead.Bbox.Top, .000000001 * filehead.Bbox.Right, .000000001 * filehead.Bbox.Bottom); horizontalGeoBlocks = (int)(fileBounds.Width / geoBlockWidth) + 1; verticalGeoBlocks = (int)(fileBounds.Height / geoBlockHeight) + 1; //Console.WriteLine("geoblocks {0}x{1}", horizontalGeoBlocks, verticalGeoBlocks); geoBlocks = new List<long>[horizontalGeoBlocks + 1, verticalGeoBlocks + 1]; wayGeoBlocks = new List<long>[horizontalGeoBlocks + 1, verticalGeoBlocks + 1]; landGeoBlocks = new List<long>[horizontalGeoBlocks + 1, verticalGeoBlocks + 1]; buildingGeoBlocks = new List<long>[horizontalGeoBlocks + 1, verticalGeoBlocks + 1]; } else { file.Position += blobHead.Datasize; blocks.Add(blockstart); } } Console.WriteLine("Reading nodes"); Parallel.ForEach(blocks, blockstart => //foreach(long blockstart in blocks) { BlobHeader blobHead; byte[] blockData; lock (file) { file.Position = blockstart; blobHead = readBlobHeader(file); // This means End Of File if (blobHead == null || blobHead.Type == "OSMHeader") throw new Exception("Should never happen"); blockData = readBlockData(file, blobHead.Datasize); } if (blobHead.Type == "OSMData") { PrimitiveBlock pb = PrimitiveBlock.ParseFrom(blockData); for (int i = 0; i < pb.PrimitivegroupCount; i++) { PrimitiveGroup pg = pb.GetPrimitivegroup(i); if (pg.HasDense) { // Remember the start of every blob with nodes nodeBlockIndexes.Insert(pg.Dense.GetId(0), blockstart); long id = 0; double latitude = 0, longitude = 0; for (int j = 0; j < pg.Dense.IdCount; j++) { id += pg.Dense.GetId(j); latitude += .000000001 * (pb.LatOffset + pb.Granularity * pg.Dense.GetLat(j)); longitude += .000000001 * (pb.LonOffset + pb.Granularity * pg.Dense.GetLon(j)); if (fileBounds.Contains(longitude, latitude)) { int blockX = XBlock(longitude); int blockY = YBlock(latitude); List<long> list = geoBlocks[blockX, blockY]; if (list == null) geoBlocks[blockX, blockY] = list = new List<long>(); list.Add(id); } } } else { wayBlocks.Add(blockstart); } } } //else //Console.WriteLine("Unknown blocktype: " + blobHead.Type); }); Console.WriteLine("Reading ways"); Parallel.ForEach(blocks, blockstart => //foreach(long blockstart in wayBlocks) { BlobHeader blobHead; byte[] blockData; lock (file) { file.Position = blockstart; blobHead = readBlobHeader(file); // This means End Of File if (blobHead == null || blobHead.Type == "OSMHeader") throw new Exception("Should never happen"); blockData = readBlockData(file, blobHead.Datasize); } if (blobHead.Type == "OSMData") { PrimitiveBlock pb = PrimitiveBlock.ParseFrom(blockData); for (int i = 0; i < pb.PrimitivegroupCount; i++) { PrimitiveGroup pg = pb.GetPrimitivegroup(i); /* * Part one: read all the curves and add them */ // Insert curves in the curve tree for (int j = 0; j < pg.WaysCount; j++) { CurveType type = CurveType.UnTested; OSMPBF.Way w = pg.GetWays(j); // Nodes in this way List<long> nodes = new List<long>(); long id = 0; for (int k = 0; k < w.RefsCount; k++) { id += w.GetRefs(k); nodes.Add(id); } string name = ""; int maxSpeed = 0; bool makeCurve = true; bool curveTypeSpecified = true; bool carSpecified = false; bool bicycleSpecified = false; bool footSpecified = false; bool busSpecified = false; bool carAllowed = false; bool bicycleAllowed = false; bool footAllowed = false; bool busAllowed = false; for (int k = 0; k < w.KeysCount; k++) { string key = pb.Stringtable.GetS( (int)w.GetKeys(k)).ToStringUtf8(); string value = pb.Stringtable.GetS( (int)w.GetVals(k)).ToStringUtf8(); #region Process keys switch (key.ToLower()) { #region highway case "highway": switch (value) { case "bus_guideway": type = CurveType.Bus_guideway; break; case "construction": type = CurveType.Construction_street; break; case "cycleway": type = CurveType.Cycleway; break; case "footway": type = CurveType.Footway; break; case "Living_street": type = CurveType.Living_street; break; case "motorway": type = CurveType.Motorway; break; case "motorway_link": type = CurveType.Motorway_link; break; case "pedestrian": type = CurveType.Pedestrian; break; case "primary": type = CurveType.Primary; break; case "primary_link": type = CurveType.Primary_link; break; case "proposed": type = CurveType.Proposed; break; case "raceway": type = CurveType.Raceway; break; case "residential": type = CurveType.Residential_street; break; case "road": type = CurveType.Road; break; case "secondary": type = CurveType.Secondary; break; case "secondary_link": type = CurveType.Secondary_link; break; case "service": type = CurveType.Service; break; case "steps": type = CurveType.Steps; break; case "tertiary": type = CurveType.Tertiary; break; case "tertiary_link": type = CurveType.Tertiary_link; break; case "track": type = CurveType.Track; break; case "trunk": type = CurveType.Trunk; break; case "trunk_link": type = CurveType.Trunk_link; break; case "unclassified": type = CurveType.Unclassified; break; case "path": type = CurveType.Path; break; default: //Console.WriteLine("TODO: highway=" + value); break; } break; #endregion #region landuse case "landuse": switch (value) { case "allotments": type = CurveType.Allotments; break; case "basin": type = CurveType.Basin; break; case "construction": type = CurveType.Construction_land; break; case "grass": type = CurveType.Grass; break; case "farm": type = CurveType.Farm; break; case "forest": type = CurveType.Forest; break; case "military": type = CurveType.Military; break; case "orchard": type = CurveType.Orchard; break; case "pond": type = CurveType.Salt_pond; break; case "recreation_centre": type = CurveType.Recreation_ground; break; default: //Console.WriteLine("TODO: landuse=" + value); break; } break; #endregion case "building": type = CurveType.Building; break; case "natural": if (value == "water") type = CurveType.Water; break; case "name": case "addr:housename": case "alt_name": case "loc_name": case "name_alt": case "name:left": case "name:right": if(name == "") name = value; else name = name + "/" + value; break; case "maxspeed": int.TryParse(value, out maxSpeed); break; case "bicycle": case "bicycle:backward": case "cyclestreet": case "cycleway": case "cycleway:lane": case "cycleway:left": case "cycleway:left:surface": case "cycleway:left:width": case "cycleway:right": case "cycleway:right:surface": case "cycleway:right:width": case "cycleway:surface": case "cycleway:width": bicycleSpecified = true; bicycleAllowed = value != "no"; break; case "bicycle:oneway": case "oneway:bicycle": case "cycleway:oneway": case "oneway:cycleway:": bicycleSpecified = true; bicycleAllowed = true; break; case "vehicle": bicycleSpecified = true; carSpecified = true; if(value == "no") { bicycleAllowed = false; carAllowed = false; } else { bicycleAllowed = true; carAllowed = true; } break; case "car": case "motorcar": case "motor_vehicle": carSpecified = true; carAllowed = value != "no"; break; case "foot": case "footway": footSpecified = true; footAllowed = value != "no"; break; case "public_transport": type = CurveType.PublicTransportPlatform; break; case "bus": if(value != "no") type = CurveType.PublicServiceVehicles; break; case "waterway": case "water": // TODO? draw these things? // Or just the lake/basin/pond? if (value == "ditch" || value == "drain" || value == "weir" || value == "stream" || value == "yes" || value == "river" || value == "culvert" || value == "drain; culvert" || value == "Ditch" || value == "Tank_ditch" || value == "dept_line" || value == "lock" || value == "canal" ) { type = CurveType.Waterway; } if ( value == "lake" || value == "basin" || value == "pond" || value == "riverbank" ) { type = CurveType.Water; } break; case "psv": if (value == "yes") type = CurveType.PublicServiceVehicles; break; case "amenity": if (value == "parking") { type = CurveType.Parking; Coordinate center = FindCentroid(nodes); extras.Add(new Location(new Node(center.Longitude, center.Latitude, 0), LocationType.Parking)); } break; case "power": if (value == "generator") type = CurveType.Power; break; default: if (key.StartsWith("building")) { type = CurveType.Building; } break; } #endregion } // Try to make sense of tags #region Process transport tags if (type.IsStreet()) { // If type props don't match specified props if(!curveTypeSpecified || (bicycleSpecified && (bicycleAllowed != type.BicyclesAllowed())) || (carSpecified && (carAllowed != type.CarsAllowed())) || (footSpecified && (footAllowed != type.FootAllowed()))) { // What is specified exactly? footAllowed = footSpecified ? footAllowed : type.FootAllowed(); bicycleAllowed = bicycleSpecified ? bicycleAllowed : type.BicyclesAllowed(); carAllowed = carSpecified ? carAllowed : type.CarsAllowed(); // Tedious matching of stuff if(carAllowed) { if(footAllowed) { if (bicycleAllowed) type = CurveType.CarBicycleFoot; } else { if(bicycleAllowed) type = CurveType.CarBicycleNoFoot; else type = CurveType.Motorway; } } else { // What is specified exactly? footAllowed = footSpecified ? footAllowed : type.FootAllowed(); bicycleAllowed = bicycleSpecified ? bicycleAllowed : type.BicyclesAllowed(); carAllowed = carSpecified ? carAllowed : type.CarsAllowed(); if(type == CurveType.PublicServiceVehicles) { if(footAllowed) { if(bicycleAllowed) type = CurveType.BusFootBicycle; else type = CurveType.BusFoot; } else type = CurveType.BusBicycle; } else { if(footAllowed && bicycleAllowed) type = CurveType.Path; if(footAllowed && !bicycleAllowed) type = CurveType.Footway; if(!footAllowed && bicycleAllowed) type = CurveType.NoCarBicycleNoFoot; if(!footAllowed && !bicycleAllowed) type = CurveType.NoneAllowed; } } } } #endregion if (makeCurve) { Curve c = new Curve(nodes.ToArray(), name); c.Name = name; c.Type = type; if (type.IsStreet()) { foreach (long n in nodes) { ways.Insert(n, c); } if (maxSpeed > 0) { c.MaxSpeed = maxSpeed; } } else { if (type.IsBuilding()) { foreach (long n in nodes) { buildings.Insert(n, c); } } else { foreach (long n in nodes) { lands.Insert(n, c); } } } } } /* * Part two: adding bus routes and the likes */ ListTree<long> endIdStartId = new ListTree<long>(); //Parallel.For(0, pg.RelationsCount, j => for(int j = 0; j < pg.RelationsCount; j++) { Relation rel = pg.GetRelations(j); bool publictransport = false; string name = ""; for (int k = 0; k < rel.KeysCount; k++) { string key = pb.Stringtable.GetS((int)rel.GetKeys(k)).ToStringUtf8(); string value = pb.Stringtable.GetS((int)rel.GetVals(k)).ToStringUtf8(); if (key == "route" && (value == "bus" || value == "trolleybus" || value == "share_taxi" || value == "tram")) publictransport = true; if (key == "ref") name = value; } if (publictransport) { long id = 0; //List<long> nodes = new List<long>(); for (int k = 0; k < rel.MemidsCount; k++) { id += rel.GetMemids(k); string role = pb.Stringtable.GetS((int)rel.GetRolesSid(k)).ToStringUtf8(); string type = rel.GetTypes(k).ToString(); //Console.WriteLine(type + " " + id + " is " + role); if (type == "NODE" && role.StartsWith("stop")) { busNodes.Add(id); busNames.Add(name); } } for (int l = 0; l < busNodes.Count - 1; l++) { Edge e = new Edge(busNodes[l], busNodes[l + 1]); e.Type = CurveType.AbstractBusRoute; if (!endIdStartId.Get(e.End).Contains(e.Start)) { abstractBusWays.Add(e); endIdStartId.Insert(e.End, e.Start); } } Edge e2 = new Edge(busNodes[busNodes.Count - 1], 0); e2.Type = CurveType.AbstractBusRoute; abstractBusWays.Add(e2); endIdStartId.Insert(busNodes[busNodes.Count - 1], 0); } } } } }); file.Close(); Console.WriteLine("Sorting nodes"); Parallel.For(0, horizontalGeoBlocks, (x) => { for(int y = 0; y <= verticalGeoBlocks; y++) { if(geoBlocks[x, y] != null) { List<long> wayList = new List<long>(); List<long> landList = new List<long>(); List<long> buildingList = new List<long>(); foreach(long id in geoBlocks[x, y]) { if(ways.Get(id).Count != 0) wayList.Add(id); if(lands.Get(id).Count != 0) landList.Add(id); if(buildings.Get(id).Count != 0) buildingList.Add(id); } wayGeoBlocks[x, y] = wayList; landGeoBlocks[x, y] = landList; buildingGeoBlocks[x, y] = buildingList; } } }); Console.WriteLine("Routing busStations"); // Add busstations for (int i = 0; i < busNodes.Count; i++) { if (busStations.Get(busNodes[i]) == null) { Node n = GetNode(busNodes[i]); if (n.Longitude != 0 && n.Latitude != 0) { busStations.Insert(busNodes[i], n); extras.Add(new Location(n, LocationType.BusStation)); } } } busThread = new Thread(new ThreadStart(() => { LoadBusses(); })); busThread.Start(); }
/// <summary> /// Control that draws the map and updates the tiles. /// </summary> public MapDisplay(int x, int y, int width, int height, LoadingThread thr) { this.Location = new Point(x, y); this.Width = width; this.Height = height; this.bounds = new BBox(5.16130, 52.06070, 5.19430, 52.09410); this.DoubleBuffered = true; this.updateStatusDelegate = new UpdateStatusDelegate(UpdateStatus); this.updateRouteStatsDelegate = new UpdateRouteStatsDelegate(UpdateRouteStats); this.startLogoDelegate = new StartLogoDelegate(StartLoadingLogo); this.stopLogoDelegate = new StopLogoDelegate(StopLoadingLogo); this.updateThread = new Thread(new ThreadStart(this.UpdateTiles)); this.MouseClick += (object o, MouseEventArgs mea) => { OnClick(o, new MouseMapDragEventArgs(null, mea.Button, mea.Clicks, mea.X, mea.Y, mea.Delta)); }; this.MouseDoubleClick += OnDoubleClick; this.Paint += OnPaint; this.Resize += OnResize; this.MouseDown += OnMouseDown; this.MouseUp += OnMouseUp; this.MouseMove += OnMouseMove; this.Disposed += (object o, EventArgs ea) => { updateThread.Abort(); }; // Thread that loads the graph. loadingThread = thr; // Checks whether the graph is loaded so the mapdisplay can start loading tiles. loadingTimer = new System.Windows.Forms.Timer(); loadingTimer.Interval = 100; loadingTimer.Tick += (object o, EventArgs ea) => { DoUpdate(); }; loadingTimer.Start(); logo = new AllstarsLogo(true); logo.Location = Point.Empty; logo.Width = this.Width; logo.Height = this.Height; this.Controls.Add(logo); logo.Start(); LinkLabel creditLabel = new LinkLabel(); creditLabel.Text = "© OpenStreetMap contributors"; creditLabel.LinkArea = new LinkArea(2, 13); creditLabel.LinkClicked += (object o, LinkLabelLinkClickedEventArgs ea) => { System.Diagnostics.Process.Start("http://www.openstreetmap.org/copyright/en"); }; creditLabel.Anchor = (AnchorStyles.Right | AnchorStyles.Bottom); creditLabel.Size = creditLabel.PreferredSize; creditLabel.Location = new Point(this.Width - creditLabel.Width - 1, this.Height - creditLabel.Height - 1); this.Controls.Add(creditLabel); statLabel = new Label(); statLabel.AutoSize = true; statLabel.Resize += (object o, EventArgs ea) => { statLabel.Location = new Point(this.Width - 1 - statLabel.Size.Width, 1); }; statLabel.Anchor = (AnchorStyles.Right | AnchorStyles.Top); statLabel.Font = new Font("Microsoft Sans Serif", 11); this.Controls.Add(statLabel); myVehicles = new List <MyVehicle>(); icons = new List <MapIcon>(); streetSelection = new List <Curve>(); tiles = new List <List <Bitmap> >(); tileCorners = new List <List <Point> >(); tiles.Add(new List <Bitmap>()); tileCorners.Add(new List <Point>()); tileIndex = 0; tileIndexes = new List <SortedList <int, SortedList <int, int> > >(); tileIndexes.Add(new SortedList <int, SortedList <int, int> >()); zoomWidth = new List <double>(); zoomHeight = new List <double>(); this.Disposed += (sender, e) => { logo.StillLoading = false; }; // Set linecaps for the pens. footPen.SetLineCap(LineCap.Round, LineCap.Round, DashCap.Round); bikePen.SetLineCap(LineCap.Round, LineCap.Round, DashCap.Round); carPen.SetLineCap(LineCap.Round, LineCap.Round, DashCap.Round); busPen.SetLineCap(LineCap.Round, LineCap.Round, DashCap.Round); otherPen.SetLineCap(LineCap.Round, LineCap.Round, DashCap.Round); }
/// <summary> /// Returns a Point in pixel-coordinates from a Node with /// geological coordinates. The Point is in coordinates for the /// entire map so it should be converted to coordinates for the /// specific tile that is drawn. /// </summary> private Point nodeToTilePoint(BBox box, Bitmap tile, Node node) { Coordinate c = new Coordinate(node.Longitude, node.Latitude); Projection p = new Projection(box.Width, tile.Width, new Coordinate(box.XMin, box.YMax)); return p.CoordToPoint(c); }
/// <summary> /// Resizes the BBox box if zoomlevel is very low. /// This is because when curves pass through a tile but no nodes /// of that curve are in that tile the curve won't be drawn in that tile. /// So on very low zoomlevels, we need to search wider to draw all /// curves in a tile. /// </summary> private BBox getSearchBBox(BBox box, int zoomLevel) { if (zoomLevel == 1) return BBox.getResizedBBox(box, 3); if (zoomLevel == 0) return BBox.getResizedBBox(box, 7); return box; }
/// <summary> /// Draws the Curve "curve" with Pen "pen" on the Bitmap "tile", /// where box represents the position and size /// of the tile in geological coordinates. /// </summary> private void drawStreet(BBox box, Bitmap tile, Curve curve, Pen pen) { Point start = nodeToTilePoint(box, tile, new Node(box.XMin, box.YMax, 0)); Graphics gr = Graphics.FromImage(tile); gr.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias; Node startNode = graph.GetNode(curve[0]); int index = 1; while (startNode.Longitude == 0 && startNode.Latitude == 0) { startNode = graph.GetNode(curve[index]); index++; } // it doesn't matter if pt2 is null at start Point pt1 = nodeToTilePoint(box, tile, startNode), pt2; for (int i = index; i < curve.AmountOfNodes; i++) { Node node = graph.GetNode(curve[i]); if (node.Longitude != 0 && node.Latitude != 0) { pt2 = nodeToTilePoint(box, tile, node); gr.DrawLine(pen, pt1.X - start.X, -pt1.Y + start.Y, pt2.X - start.X, -pt2.Y + start.Y); pt1 = pt2; } } }
/// <summary> /// Draws a piece of land or building using Brush "brush" /// on the Bitmap "tile", where box represents the position /// and size of the tile in geological coordinates. /// </summary> private void drawLanduse(BBox box, Bitmap tile, Curve curve, Brush brush) { List<Point> polygonPoints = new List<Point>(); Point start = nodeToTilePoint(box, tile, new Node(box.XMin, box.YMax, 0)); Graphics gr = Graphics.FromImage(tile); gr.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias; for (int i = 0; i < curve.AmountOfNodes; i++) { Node node = graph.GetNode(curve[i]); if (node.Longitude != 0 && node.Latitude != 0) { Point p = nodeToTilePoint(box, tile, node); polygonPoints.Add(new Point(p.X - start.X, -p.Y + start.Y)); } } gr.FillPolygon(brush, polygonPoints.ToArray()); }
public Curve[] GetLandsInBBox(BBox box) { Node[] curveNodes = GetLandNodesInBBox(box); HashSet<Curve> set = new HashSet<Curve>(); foreach(Node n in curveNodes) { foreach (Curve curve in lands.Get(n.ID)) { set.Add(curve); } } List<Curve> res = new List<Curve>(); res.AddRange(set); return res.ToArray(); }
public Location[] GetExtrasInBBox(BBox box) { List<Location> res = new List<Location>(); foreach (Location l in extras) { if (box.Contains(l.Longitude, l.Latitude)) res.Add(l); } return res.ToArray(); }
/// <summary> /// Zooms in on point (x,y) on the screen with a factor of 'factor'. /// If factor > 1, zooms in. /// If factor < 1, zooms out. /// </summary> private void Zoom(int x, int y, float factor) { if (!lockZoom) { //return if you already have zoomed in to the max if ((factor > 1) && (Renderer.GetZoomLevel(LonFromX(0), LonFromX(bmpWidth), bmpWidth) < 1)) return; Point upLeft = CoordToPoint(bounds.XMin, bounds.YMax); double fracX = (double)x / this.Width; double fracY = (double)y / this.Height; double w = (int)(this.Width / factor); double h = (int)(this.Height / factor); int xMin = (int)(x - fracX * w); int yMin = (int)(y - fracY * h); int xMax = (int)(xMin + w); int yMax = (int)(yMin + h); Coordinate cUpLeft = PointToCoord(xMin + upLeft.X, upLeft.Y - yMin); Coordinate cDownRight = PointToCoord(xMax + upLeft.X, upLeft.Y - yMax); // When the user zooms in/out, the tiles on that zoomlevel are saved // and there will be created a new list where new tiles will be added. // When the user goes back to the old zoomLevel, the old tiles can be used again. if (factor > 1) { if (tileIndex - 1 >= 0) { cDownRight = new Coordinate(cUpLeft.Longitude + zoomWidth[tileIndex - 1], cUpLeft.Latitude - zoomHeight[tileIndex - 1]); } else { zoomWidth.Insert(0, Math.Abs(cUpLeft.Longitude - cDownRight.Longitude)); zoomHeight.Insert(0, Math.Abs(cUpLeft.Latitude - cDownRight.Latitude)); } if (tileIndex > 0) { tileIndex--; } else { tiles.Insert(0, new List<Bitmap>()); tileCorners.Insert(0, new List<Point>()); tileIndexes.Insert(0, new SortedList<int, SortedList<int, int>>()); } } else { tileIndex++; if (tileIndex < zoomWidth.Count) { cDownRight = new Coordinate(cUpLeft.Longitude + zoomWidth[tileIndex], cUpLeft.Latitude - zoomHeight[tileIndex]); } else { zoomWidth.Insert(tileIndex, Math.Abs(cUpLeft.Longitude - cDownRight.Longitude)); zoomHeight.Insert(tileIndex, Math.Abs(cUpLeft.Latitude - cDownRight.Latitude)); } if (tileIndex >= tiles.Count) { tiles.Insert(tileIndex, new List<Bitmap>()); tileCorners.Insert(tileIndex, new List<Point>()); tileIndexes.Insert(tileIndex, new SortedList<int,SortedList<int,int>>()); } } bounds = new BBox(cUpLeft.Longitude, cUpLeft.Latitude, cDownRight.Longitude, cDownRight.Latitude); forceUpdate = true; this.DoUpdate(); } }
public Node[] GetLandNodesInBBox(BBox box) { // Only search if we have data about this area if(!box.IntersectWith(fileBounds)) return new Node[0]; List<Node> nds = new List<Node>(); int xStart = XBlock(box.XMin); int xEnd = XBlock(box.XMax); int yStart = YBlock(box.YMin); int yEnd = YBlock(box.YMax); if(xStart < 0) xStart = 0; if(xEnd >= horizontalGeoBlocks) xEnd = horizontalGeoBlocks; if(yStart < 0) yStart = 0; if(yEnd >= verticalGeoBlocks) yEnd = verticalGeoBlocks; for(int x = xStart; x <= xEnd; x++) { for(int y = yStart; y <= yEnd; y++) { if(landGeoBlocks[x, y] != null) { foreach (long id in landGeoBlocks[x, y]) { Node nd = GetNode(id); if (box.Contains(nd.Longitude, nd.Latitude)) { nds.Add(nd); } } } } } return nds.ToArray(); }
/// <summary> /// Creates a RouteFinder and a Renderer when needed. /// Starts and stops the UpdateThread if needed. /// </summary> private void DoUpdate() { if (graph == null) { graph = loadingThread.Graph; if (graph != null) { // Set bounds to filebounds. BBox fileBounds = graph.FileBounds; Point p1 = CoordToPoint(fileBounds.XMax, fileBounds.YMax); Point p2 = CoordToPoint(fileBounds.XMin, fileBounds.YMin); int w = Math.Abs(p1.X - p2.X); int h = Math.Abs(p1.Y - p2.Y); if ((float)h / w > (float)this.Height / this.Width) { this.bounds = new BBox(fileBounds.XMin, fileBounds.YMax, fileBounds.XMin + LonFromX(h), fileBounds.YMin); } else { this.bounds = new BBox(fileBounds.XMin, fileBounds.YMax, fileBounds.XMax, fileBounds.YMax - LatFromY(LonToX(fileBounds.YMin) + h)); } zoomWidth.Add(bounds.Width); zoomHeight.Add(bounds.Height); } } else { if (rf == null || render == null) { rf = new RouteFinder(graph); render = new Renderer(graph); loadingTimer.Stop(); logo.Stop(); this.Controls.Remove(logo); updateThread.Start(); } // If the updateThread is running and this method is called, // just let the thread restart when it's finished the current tile. if (forceUpdate && updateThread.ThreadState == ThreadState.Running) { restartUpdateThread = true; forceUpdate = false; } // If the updateThread is stopped and this method is called, // then just start the thread. if (updateThread.ThreadState == ThreadState.Stopped) { updateThread = new Thread(new ThreadStart(this.UpdateTiles)); updateThread.Start(); } this.Invalidate(); } }
/// <summary> /// Return true if the boundingbox intersects with "box". /// </summary> public bool IntersectWith(BBox box) { return box.Contains(xMin, yMin) || box.Contains(xMin, yMax) || box.Contains(xMax, yMin) || box.Contains(yMax, yMax) || this.Contains(box.XMin, box.YMin) || this.Contains(box.XMin, box.YMax) || this.Contains(box.XMax, box.YMin) || this.Contains(box.XMax, box.YMax); }
/// <summary> /// Returns true if the tileCorner with index 'id' is in the screen. /// </summary> private bool IsInScreen(int id) { if (id >= tileCorners[tileIndex].Count) return false; Coordinate c1 = PointToCoord(tileCorners[tileIndex][id].X, tileCorners[tileIndex][id].Y); Coordinate c2 = PointToCoord(tileCorners[tileIndex][id].X + bmpWidth, tileCorners[tileIndex][id].Y + bmpHeight); BBox box = new BBox(c1.Longitude, c1.Latitude, c2.Longitude, c2.Latitude); return this.bounds.IntersectWith(box); }
/// <summary> /// Zooms in on point (x,y) on the screen with a factor of 'factor'. /// If factor > 1, zooms in. /// If factor < 1, zooms out. /// </summary> private void Zoom(int x, int y, float factor) { if (!lockZoom) { //return if you already have zoomed in to the max if ((factor > 1) && (Renderer.GetZoomLevel(LonFromX(0), LonFromX(bmpWidth), bmpWidth) < 1)) { return; } Point upLeft = CoordToPoint(bounds.XMin, bounds.YMax); double fracX = (double)x / this.Width; double fracY = (double)y / this.Height; double w = (int)(this.Width / factor); double h = (int)(this.Height / factor); int xMin = (int)(x - fracX * w); int yMin = (int)(y - fracY * h); int xMax = (int)(xMin + w); int yMax = (int)(yMin + h); Coordinate cUpLeft = PointToCoord(xMin + upLeft.X, upLeft.Y - yMin); Coordinate cDownRight = PointToCoord(xMax + upLeft.X, upLeft.Y - yMax); // When the user zooms in/out, the tiles on that zoomlevel are saved // and there will be created a new list where new tiles will be added. // When the user goes back to the old zoomLevel, the old tiles can be used again. if (factor > 1) { if (tileIndex - 1 >= 0) { cDownRight = new Coordinate(cUpLeft.Longitude + zoomWidth[tileIndex - 1], cUpLeft.Latitude - zoomHeight[tileIndex - 1]); } else { zoomWidth.Insert(0, Math.Abs(cUpLeft.Longitude - cDownRight.Longitude)); zoomHeight.Insert(0, Math.Abs(cUpLeft.Latitude - cDownRight.Latitude)); } if (tileIndex > 0) { tileIndex--; } else { tiles.Insert(0, new List <Bitmap>()); tileCorners.Insert(0, new List <Point>()); tileIndexes.Insert(0, new SortedList <int, SortedList <int, int> >()); } } else { tileIndex++; if (tileIndex < zoomWidth.Count) { cDownRight = new Coordinate(cUpLeft.Longitude + zoomWidth[tileIndex], cUpLeft.Latitude - zoomHeight[tileIndex]); } else { zoomWidth.Insert(tileIndex, Math.Abs(cUpLeft.Longitude - cDownRight.Longitude)); zoomHeight.Insert(tileIndex, Math.Abs(cUpLeft.Latitude - cDownRight.Latitude)); } if (tileIndex >= tiles.Count) { tiles.Insert(tileIndex, new List <Bitmap>()); tileCorners.Insert(tileIndex, new List <Point>()); tileIndexes.Insert(tileIndex, new SortedList <int, SortedList <int, int> >()); } } bounds = new BBox(cUpLeft.Longitude, cUpLeft.Latitude, cDownRight.Longitude, cDownRight.Latitude); forceUpdate = true; this.DoUpdate(); } }