Skip to content

Archive

Category: Tutorials

When using sorting with the ASP.NET gridview, the header cell of a column contains a link (generated internally by .NET using a LinkButton).

Everything else in the gridview is easily styled by readily accessable CssClass tags. This is not quite so easy to accomplish for the sort link in the header of a column.

To acheive this, we need to catch the RowCreated event and manually find the header we are looking for, by comparing the SortExpression of each column’s header link with the SortExpression of the gridview which gets set when you sort the gridview.

Once you have found the LinkButton associated with the column, it is an easy matter to set the class using the LinkButton‘s CssClass property.

protected void GridView1_RowCreated(object sender, GridViewRowEventArgs e)
{
    if (e.Row.RowType == DataControlRowType.Header)
    {
        foreach (TableCell tc in e.Row.Cells)
        {
            if (tc.HasControls())
            {
                LinkButton headerLink = (LinkButton)tc.Controls[0];
                if (GridView1.SortExpression == headerLink.CommandArgument)
                    headerLink.CssClass = "sorted";

                if (SortDirection == SortDirection.Descending)
                    headerLink.CssClass += " desc";
                else
                    headerLink.CssClass += " asc";
            }
        }
    }
}
Share

Now that we know how gedcom files work (covered in part 1), we need to actually parse the file.

The way in which the gedcom is structured is that each new record entry starts on a line with a 0. All lines with a 1 under that belong to that record. Similarly, lines with a 2 prefix, belond to the the entry prefixed by a 1 directly above it.

This makes processing rather straightforward. All we need to do, is read the file line by line, then split up the line into its components, and process accordingly.

All lines have a number (0, 1 or 2) as the first field in the line, and each field is seperated by a space, for example 1 SOUR FamilyTraces

The second field in the line is usually the keyword which tells us what the data represents. Some keywords will not need an extra data field in the third field, but usually, these types of lines which are grouped together with that line.
As an example, the VERS and FORM lines are linked to the GEDC keyword as the 2 in the first field shows they belong to the preceding line.

1 GEDC
2 VERS 5.5
2 FORM LINEAGE-LINKED

There is an exception to the second field being the keyword, and that is in the individual, family and note record entry fields which take the format 0 @Ixxx@ INDI for an individual record for example.

Based on all of that, all we need to do, is split up the line into the three fields, and then process each keyword independantly from each other, while keeping track of which type of record we are currently in, so that we can assign the data to the correct structure.

Here is the full code for the parser, that works pretty much exactly as described above. I have not implemented the full spec of the gedcom file format, but only enough as I need for the application. It is easily modified, however, to add the rest of the spec in if needed.

Usually, now would be the time where I list the source code for the code I have talked about in this blog post, but due to some bug with WordPress and the plug-in I am using to render my code samples nicely, the code for this section is crashing Firefox, so I have included the code for the GedcomParser class as a link to a text file containing the code.

Share

In my previous post in this series, I introduced the Family Traces family tree application. Now, I am going to go through the importing of a gedcom file.

Firstly, what is a gedcom file?

The gedcom file format is a text file format for the transmission and storage of genealogical data, created by the Church of the Latter Day Saints for their genealogical projects.

A standard gedcom file has a header section, individual records, family records and notes. The various records are linked to each other by ids, in the format of @I1@ for an individual record for example. For a full spec of the gedcom format, have a look here.

Here is a sample gedcom file

0 HEAD
1 SOUR FamilyTraces
2 NAME Family Traces
2 CORP Serge Meunier
1 DEST Standard GEDCOM
1 DATE 2010/02/16
1 CHAR ANSEL
1 GEDC
2 VERS 5.5
2 FORM LINEAGE-LINKED
0 @I1@ INDI
1 John/Smith/
2 GIVN John
2 SURN Smith
1 SEX M
1 BIRT
2 DATE 15 dec 1950
2 PLAC
1 FAMC @F2@
1 FAMS @F1@
0 @I2@ INDI
1 Jane/Doe/
2 GIVN Jane
2 SURN Doe
1 SEX F
1 FAMC @F-1@
0 @F1@ FAM
1 HUSB @I1@
1 WIFE @I2@
1 CHIL @F3@
0 TRLR

Before we start processing the file, we need to have some data structures to hold the data from the file.

    public struct GedcomHeader
    {
        public string Source;
        public string SourceVersion;
        public string SourceName;
        public string SourceCorporation;
        public string Destination;
        public string Date;
        public string File;
        public string CharacterEncoding;
        public string GedcomVersion;
        public string GedcomForm;
    }

    public struct GedcomIndividual
    {
        public string Id;
        public string GivenName;
        public string Surname;
        public string Suffix;
        public string Sex;
        public string BirthDate;
        public string BirthPlace;
        public string Occupation;
        public string Description;
        public string Nationality;
        public string DiedDate;
        public string DiedPlace;
        public string DiedCause;
        public string ParentFamilyId;
        public string SpouseFamilyId;
        public ArrayList Notes;

        public GedcomIndividual(string id)
        {
            Id = id;
            GivenName = "";
            Surname = "";
            Suffix = "";
            Sex = "";
            BirthDate = "";
            BirthPlace = "";
            Occupation = "";
            Description = "";
            Nationality = "";
            DiedDate = "";
            DiedPlace = "";
            DiedCause = "";
            ParentFamilyId = "";
            SpouseFamilyId = "";
            Notes = new ArrayList();
        }
    }

    public struct GedcomFamily
    {
        public string Id;
        public string HusbandId;
        public string WifeId;
        public string MarriageDate;
        public string MarriagePlace;
        public ArrayList Children;
        public ArrayList Notes;

        public GedcomFamily(string id)
        {
            Id = id;
            HusbandId = "";
            WifeId = "";
            MarriageDate = "";
            MarriagePlace = "";
            Children = new ArrayList();
            Notes = new ArrayList();
        }
    }

    public struct GedcomNote
    {
        public string Id;
        public string Text;

        public GedcomNote(string id)
        {
            Id = id;
            Text = "";
        }
    }

We also need a set of enumerations we will use to keep track of where we are in the file. This is important because the meaning of a field in the file is dependant on the place in the file it occurs, so we need to be aware of what we need to do at all times, but more on that later.

    public enum GedcomRecordEnum
    {
        None,
        Header,
        Individual,
        Family,
        Note
    }

    public enum GedcomSubRecordEnum
    {
        None,
        HeaderSource,
        HeaderGedcom,
        IndividualName,
        IndividualBirth,
        IndividualDeath,
        FamilyChildren,
        FamilyMarriage
    }

The full source is available Here.
Now we need to get to parsing the file, which is covered in Importing a Gedcom file – Part 2

Share

I love being able to trace my family tree, and have used several good genealogy applications which I have found very useful, including Family Tree Legends and Family Tree Builder. The problem I have though, is that they do not always contain everything which I am looking for, so I decided to write a family tree application to plug in the holes.

Family Traces, which you can download the full source for Here, is a fully functional, albeit bare-boned family tree application, which can import and export Gedcom files, calculate ancestors and descendants, and allow you to edit the family tree. Everything is there for a starting point, from where you can develop further reports and functionality.

All of that will come in later posts in this series though. For now, I am going to focus on the core of the application – the data layout. This layout is created within an Access database included with the application.

The two main components of any family tree are individuals and families.

Starting with the individual, each person in the family tree will have a record in the database. The most important data we need for an individual are:

* Individual Id
* Name
* Birth date
* Birth place
* Death date
* Death place

These fields are vital in determining if the person we are looking at is the same person listed in genealogical records, such as birth certificates, genealogical lists etc.

Now data collection for an individual is not restricted to these fields, and you are free to create as many fields as your want for things like physical attributes, cause of death, photos and general notes.

Moving onto families now, a family is what connects different individuals together into a tree.

The main family record will have

* Family Id
* Husband Id
* Wife Id
* Marriage date
* Marriage place

This links the husband and wife individual records into the family, and allows you to track the marriage date and place, which is also important in genealogical research, as it may provide crucial hints to the individuals, particularly if the birth and death dates of the individuals are unsure.

The last thing we need is to be able to link individuals as children within a family, and this is done using a separate table, which keeps track of the family id, as well as the individual id of the child, and each family can have multiple individuals attached to it as children.

In the database setup for the application, I also included a field in the individual record for the parent family record, which makes it easier to link the individual to a family as a child when processing the tree. This means that we are able to find the family that the individual is a child of without having to check the family records directly.

Now that is all there is to the core of a genealogy application, so feel free to play around with the application, and I will explain more about how the gedcom file format works in the next tutorial…

Share

Decreasing the colour depth involves converting colour values to standard values.

Specifying an offset, preferably a value evenly divisible by 256, such as 16, 24 or 32, we then need to make sure that the red, green and blue components’ values get rounded off to multiples of this offset.

For example, using an offset of 16, value colour values would be 0, 15, 31, 47,……, 255. and all values would need to be rounded off to these values for each of the colour components.

To be able to do this, we take the component value, and then add half of the offset to the value. We then subtract the modulo of this value and the offset. This then gives values along the lines of 0, 16, 32, …., 256, so we will need to subtract 1 from this, but will need to put in a correction for 0, since that value needs to remain as it is.

Decreasing the colour depth using an offset of 16

Decreasing the colour depth using an offset of 16


You can download the full code for the sample application which contains code for all the image effects covered in the series here.

public void ApplyDecreaseColourDepth(int offset)
{
    int A, R, G, B;
    Color pixelColor;

    for (int y = 0; y < bitmapImage.Height; y++)
    {
        for (int x = 0; x < bitmapImage.Width; x++)
        {
            pixelColor = bitmapImage.GetPixel(x, y);
            A = pixelColor.A;
            R = ((pixelColor.R + (offset / 2)) - ((pixelColor.R + (offset / 2)) % offset) - 1);
            if (R < 0)
            {
                R = 0;
            }
            G = ((pixelColor.G + (offset / 2)) - ((pixelColor.G + (offset / 2)) % offset) - 1);
            if (G < 0)
            {
                G = 0;
            }
            B = ((pixelColor.B + (offset / 2)) - ((pixelColor.B + (offset / 2)) % offset) - 1);
            if (B < 0)
            {
                B = 0;
            }
            bitmapImage.SetPixel(x, y, Color.FromArgb(A, R, G, B));
        }
    }

}
Share

Back in the old days, and here I am talking about the 19th century old days, it was very common to apply a sepia tinting to photographs. This was a chemical process, which preserved the photos for longer, although it was also used for its aesthetic appeal.

These days, with digital photograpy, we don’t need to preserve photos like in those early days, but it still does give a nice warm tone to a photo, and is great for making a modern photo appear old-fashioned.

Converting a photo to sepia is a relatively easy effect.

For each pixel in the image, we first convert the pixel to greyscale. We then add a value to the green component, and double that value to the red component to give the sepia effect.

The depth of the sepia effect is determined by this value, which we pass here as a parameter. A value of 0 will give a standard greyscale image.

Applying a Sepia value of 20

Applying a Sepia value of 20


You can download the full code for the sample application which contains code for all the image effects covered in the series here.

public void ApplySepia(int depth)
{
    int A, R, G, B;
    Color pixelColor;

    for (int y = 0; y < bitmapImage.Height; y++)
    {
        for (int x = 0; x < bitmapImage.Width; x++)
        {
            pixelColor = bitmapImage.GetPixel(x, y);
            A = pixelColor.A;
            R = (int)((0.299 * pixelColor.R) + (0.587 * pixelColor.G) + (0.114 * pixelColor.B));
            G = B = R;

            R += (depth * 2);
            if (R > 255)
            {
                R = 255;
            }
            G += depth;
            if (G > 255)
            {
                G = 255;
            }

            bitmapImage.SetPixel(x, y, Color.FromArgb(A, R, G, B));
        }
    }
}

Share

One of the most striking effects to apply to an image is embossing. This is effect is a form of edge-detection, and is also done using convolution, which you can read more about on my post on Smoothing using convolution.

The convolution grid we use in embossing is a little different to the others we have looked at so far. It has an offset of 127, and a fixed factor of 4. The offset makes the the image appear on average as a medium gray.

The grid we will use then is

-1 0 -1
0 4 0
-1 0 -1

Embossing

Embossing


You can download the full code for the sample application which contains code for all the image effects covered in the series here.

public void ApplyEmboss(double weight)
{
    ConvolutionMatrix matrix = new ConvolutionMatrix(3);
    matrix.SetAll(1);
    matrix.Matrix[0, 0] = -1;
    matrix.Matrix[1, 0] = 0;
    matrix.Matrix[2, 0] = -1;
    matrix.Matrix[0, 1] = 0;
    matrix.Matrix[1, 1] = weight;
    matrix.Matrix[2, 1] = 0;
    matrix.Matrix[0, 2] = -1;
    matrix.Matrix[1, 2] = 0;
    matrix.Matrix[2, 2] = -1;
    matrix.Factor = 4;
    matrix.Offset = 127;
    bitmapImage = Convolution3x3(bitmapImage, matrix);

}
Share