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.



