Norgeskart
Programmet er organisert slik at Form1 tar seg av dialogen og holder lister av nødvendige data. Det er laget en egen klasse GeoUnit for å representere hver kommune.
Form
public partial class Form1 : Form { // transformation matrix, set on draw and used on click Matrix TM; // all polygon points List<Point> pntList = new List<Point>(3200); // all kommunes, as Kommune-instances List<Kommune> kommuner = new List<Kommune>(450); // list of fylke (and country=0) fylke[] fylkeList; // selected fylke (or Norge=0) int selectedFylke = 0; protected String hilitedName = ""; protected int hilitedIx = -1; protected String clickFoundName = ""; protected String clickFoundId = ""; protected int clickFoundIx=-1; public Form1() { InitializeComponent(); InitializeMap(); } protected void InitializeMap() { // load necessary data and prepare the datastructure // fill up pntList from data in properties // and find surrounding rectangle String t = Properties.Resources.pnts; String[] list = t.Split('\n'); int MinX = int.MaxValue; int MaxX = int.MinValue; int MinY = int.MaxValue; int MaxY = int.MinValue; for (int ix = 0; ix < list.Length; ix++) { if(list[ix].IndexOf(',')==-1) continue; Point p = new Point(Convert.ToInt32(list[ix].Split(',')[0]), Convert.ToInt32(list[ix].Split(',')[1])); if (p.X < MinX) MinX = p.X; if (p.X > MaxX) MaxX = p.X; if (p.Y < MinY) MinY = p.Y; if (p.Y > MaxY) MaxY = p.Y; pntList.Add(p); } fylkeList = new fylke[21]; // norge as fylke 0 fylke fy = new fylke(); fy.id = "00"; fy.name = "Norge"; fy.border = new Rectangle(MinX, MinY, MaxX - MinX, MaxY - MinY); fylkeList[0]=fy; // make kommune index t = Properties.Resources.kindex; list = t.Split('\n'); for (int ix = 0; ix < list.Length; ix++) if (list[ix].Trim().Length > 1) kommuner.Add(new Kommune(list[ix],pntList)); // set up fylker (assume resource fylker sorted) String fs = Properties.Resources.fylker; String[] flist = fs.Split('\n'); for (int fix = 1; fix < 21; fix++) { if (flist[fix-1].IndexOf(':') == -1) continue; fy = new fylke(); String s = Convert.ToString(fix); if (s.Length == 1) s = "0" + s; fy.id = s; fy.name = flist[fix-1].Split(':')[1].Trim(); if (fix == 13)// does not exist (was Bergen ?) fy.border = new Rectangle(1, 1, 1, 1); else fy.border = MakeBorder(fy.id); fylkeList[fix]=fy; } // fill combobox for fylke (and country) selection for (int cix = 0; cix < fylkeList.Length; cix++) { if(cix==13) continue; comboBoxView.Items.Add(fylkeList[cix].name); } comboBoxView.SelectedIndex = 0; } // prepare border for a fylker protected Rectangle MakeBorder(String id) { // run kommuneliste and identify borders of actual fylke int MinX=int.MaxValue; int MaxX=int.MinValue; int MinY=int.MaxValue; int MaxY=int.MinValue; for (int kix = 0; kix < kommuner.Count; kix++) { Kommune kom = (Kommune)kommuner[kix]; String kid = kom.Id; // is it in correct fylke ? if (kid.Substring(0, 2).CompareTo(id)==0) { Rectangle kR = kom.Border; MinX = Math.Min(MinX, kR.Left); MinY = Math.Min(MinY, kR.Top); MaxX = Math.Max(MaxX, kR.Right); MaxY = Math.Max(MaxY, kR.Bottom); } } return new Rectangle(MinX,MinY,MaxX-MinX,MaxY-MinY); } // draw everything public void DrawMap(Graphics g, Rectangle R,Rectangle modelR) { // do necessary transformation g.ResetTransform(); g.TranslateTransform(-modelR.Left, modelR.Bottom, MatrixOrder.Append); g.ScaleTransform(1.0f, -1.0f, MatrixOrder.Prepend); float scalefactor = 0.99f * Math.Min((float)R.Height / (float)modelR.Height, (float)R.Width / (float)modelR.Width); g.ScaleTransform(scalefactor, scalefactor, MatrixOrder.Append); // remember matrix TM = g.Transform; //transform is set, we draw all kommuner for (int ix = 0; ix < kommuner.Count; ix++) { List<Point> plist = kommuner[ix].Index; Point[] polygon = plist.ToArray(); String kName=kommuner[ix].Name; String kId = kommuner[ix].Id; if (kName.CompareTo(hilitedName) == 0) g.FillPolygon(new SolidBrush(Color.Blue), polygon); else if (kName.CompareTo(clickFoundName) == 0) g.FillPolygon(new SolidBrush(Color.Red), polygon); else if ((kId.Substring(0,2). CompareTo(fylkeList[selectedFylke].id)==0) || (selectedFylke==0)) g.FillPolygon(new SolidBrush(Color.White), polygon); g.DrawPolygon(new Pen(Color.Black), polygon); } } // where have we clicked public Boolean FindWhere(int x, int y, Rectangle R,Rectangle modelR) { // must translate the clickpnt to model coord Matrix m = TM.Clone(); m.Invert(); Point[] pts = new Point[1] { new Point(x, y) }; m.TransformPoints(pts); int testCount = 0; clickFoundName = "None"; while (testCount++ < kommuner.Count+1) for (int kix=0;kix<kommuner.Count;kix++) { if (kommuner[kix].GetHit(pts[0].X, pts[0].Y)) { clickFoundName = kommuner[kix].Name; clickFoundId = kommuner[kix].Id; clickFoundIx = kix; return true; } } // not hit any kommune clickFoundIx = -1; return false; } private void panelMap_Paint(object sender, PaintEventArgs e) { // paint map DrawMap(e.Graphics, panelMap.Bounds, fylkeList[selectedFylke].border); } private void InvalidateKommune(int kix) { if (kix != -1) { Rectangle r = kommuner[kix].Border; Point[] pts = new Point[2] { new Point(r.Left, r.Top), new Point(r.Right, r.Bottom) }; TM.TransformPoints(pts); panelMap.Invalidate(new Rectangle(pts[0], new Size(pts[1].X - pts[0].X, pts[1].Y - pts[0].Y))); } } // click on map private void panelMap_MouseClick(object sender, MouseEventArgs e) { // clicked on the map // invalidate the one hilited now InvalidateKommune(clickFoundIx); if (FindWhere(e.X, e.Y, panelMap.Bounds, fylkeList[selectedFylke].border)) { labelName.Text = clickFoundId + " : " + clickFoundName; Rectangle r = new Rectangle(e.X - 100, e.Y - 100, 200, 200); // invalidate the new hilite InvalidateKommune(clickFoundIx); } } // click on select button private void buttonFind_Click(object sender, EventArgs e) { // clicked the show button InvalidateKommune(hilitedIx); String who = textBoxLook.Text; hilitedIx = -1; if ((who != null)&&(who.Length>1)) { //make start with uppercase hilitedName = who.Substring(0, 1).ToUpper() + who.Substring(1); hilitedIx = FindKommuneIxByName(who); } InvalidateKommune(hilitedIx); } private int FindKommuneIxByName(string kname) { kname = kname.ToUpper(); for (int ix = 0; ix < kommuner.Count; ix++) { string n = kommuner[ix].Name.ToUpper(); if (n.CompareTo(kname) == 0) return ix; } return -1; } //change fylke (or Norway) private void comboBoxView_SelectedIndexChanged(object sender, EventArgs e) { String name = comboBoxView.SelectedItem.ToString(); for(int ix=0;ix<fylkeList.Length;ix++) if(name.CompareTo(fylkeList[ix].name)==0) { selectedFylke = ix; // invalidate everything panelMap.Invalidate(); } } private void panelMap_Resize(object sender, EventArgs e) { panelMap.Invalidate(); }
Fylker og Norge er beskrevet ved en struct:
struct fylke { public String name; public String id; public Rectangle border; }
Kommune
class Kommune { protected String name; //Halden protected String id; //0101 List<Point> polypnts; protected Rectangle border; protected GraphicsPath grapath; public Kommune(String line,List<Point> pntList) { // line is kid:name:pointix,... id = line.Split(':')[0]; if (id.Length == 3) id = "0" + id; name=line.Split(':')[1]; String[] ilist = line.Split(':')[2].Split(','); polypnts = new List<Point>(ilist.Length); int maxx = Int32.MinValue; int minx = Int32.MaxValue; int maxy = Int32.MinValue; int miny = Int32.MaxValue; for (int ix = 0; ix < ilist.Length; ix++) { int v=Convert.ToInt32(ilist[ix]); Point p = (Point)pntList[v]; maxx = Math.Max(p.X,maxx); maxy = Math.Max(p.Y,maxy); minx = Math.Min(p.X, minx); miny = Math.Min(p.Y, miny); polypnts.Add(p); } border = new Rectangle(minx, miny, maxx - minx, maxy - miny); grapath = new GraphicsPath(); Point[] Pt = (Point[])polypnts.ToArray(); grapath.AddPolygon(Pt); } public List<Point> Index { get { return polypnts; } } public Rectangle Border { get { return border; } } public String Name { get { return name; } } public string Id { get { return id; } }
Mekanismen for å sjekke om vi har pekt på en kommune kan lages på mange måter. Vi kan enkelt teste på om et punkt er inne i et rektangel med metoden R.contains(p.X,p.Y), der R er et rektangel og p er et punkt. Videre er det en del rutiner som kan sjekke overlapping av regioner i et grafisk område. Det som er valgt er en allmen, håndskrevet, algoritme for å sjekke om et punkt er inne i et vilkårlig, lukket polygon. Implementasjonen ligger i Geounit:
public Boolean GetHit(int x, int y) { if (!border.Contains(x, y)) return false; return InsidePolygon(x, y); } // handmade inside test for polygon private bool InsidePolygon(int x, int y) { // Returns TRUE if p is inside pol // pol can be convex or concave // result is not interpretable if pol has crossing lines if (polypnts.Count < 3) return false; // Count intersections to the left of p // odd is hit, even is miss int pix; Point p1 = new Point(); Point p2 = new Point(); Point ps = new Point(); bool Inside = false; // close the polygon polypnts.Add(polypnts[0]); p1.X = border.Left - 10; // smaller than the smallest p1.Y = y; p2.X = border.Right + 10; // bigger than the biggest p2.Y = y; for (pix = 0; pix < polypnts.Count - 1; pix++) if (Intersection(p1, p2, (Point)polypnts[pix], (Point)polypnts[pix + 1], ref ps)) { if (ps.X < x) Inside = !Inside; } // unclose the polygon polypnts.RemoveAt(polypnts.Count - 2); return Inside; } private bool Intersection(Point p1, Point p2, Point p3, Point p4, ref Point ps) { // finds the intersecton between lines p1-p2 and p3-p4 // return TRUE if intersection, FALSE else // result in ps, if intersection int dx1 = p2.X - p1.X; int dx2 = p4.X - p3.X; int dy1 = p2.Y - p1.Y; int dy2 = p4.Y - p3.Y; int n = dx2 * dy1 - dy2 * dx1; if (n == 0) return false; double s = 1.0 * (dx1 * (p3.Y - p1.Y) - dy1 * (p3.X - p1.X)) / (1.0 * n); if ((s < 0.0) || (s > 1.0)) return false; double t = 1.0 * (dx2 * (p3.Y - p1.Y) - dy2 * (p3.X - p1.X)) / (1.0 * n); if ((t < 0.0) || (t > 1.0)) return false; ps.X = (int)(dx1 * t + p1.X); ps.Y = (int)(dy1 * t + p1.Y); return true; }
Ut på tur
Prosjektet er bearbeidet litt til en variant der vi kan markere to kommuner og forsøke å finne en vei mellom dem. "Veinettet" er basert på at det går en vei mellom sentrum i to nabokommuner og har altså ikke noe med det reelle verinettrt å gjøre.
Programmet illustrerer en enkel variant av en søkealgoritme.