Basics

The CartoType core library is written in C++. To use it to draw maps you need to create various class objects. All CartoType functionality is accessed by calling member functions of those objects.

C++ programming conventions

Names

CartoType class names nearly all start with C or T. C stands for 'class' or 'complicated' and is for large classes which allocate memory on the heap, and which are usually created on the heap. T stands for 'type' and is for smaller, simpler classes which are usually created on the stack. Examples: CEngine, TResult.

M classes are 'mixins' or interfaces or abstract classes. Example: MPath.

Formal arguments (parameters) of functions start with a small a, for 'argument'. Example: TResult LoadTypefaceResource(const char* aName).

Data members start with a small 'i', for 'instance'. Example: TPoint::iX.

Indenting and bracketing follow the Whitesmiths style.

These conventions are documented for your convenience in understanding CartoType APIs. Naturally you are free to use your own preferred style in your code.

Construction using a 'New' function

If a class has a constructor which might fail, it is made private, and instead of using the constructor you call a static function called New, which returns a smart pointer to a fully constructed object; the pointer will be null if construction fails. For example, you can call

static std::unique_ptr<CFramework> New(TResult& aError,const CFramework::TParam& aParam)

to create the CartoType framework object. If construction fails, the returned unique pointer is null and the error code is placed in aError. If construction succeeds, aError will have the value 0 (KErrorNone).

The C++ language

CartoType is written in C++11, which is the most recent version of C++ to be fully supported on all major platforms. Exceptions are used in general only for out-of-memory conditions. Error codes are returned using the TResult type, which is a plain integer in release builds. The value 0 (KErrorNone) indicates success.

Initializing CartoType

Here is how you initialize CartoType.

Create a framework object

The framework object (CartoType::CFramework) manages the resources and objects you need to draw a map: the CartoType engine, the fonts, the data sources and the map itself.The normal system is to create just one framework object. Keep the framework for the duration of your application: it is best to make it a data member of your main application object.

You supply map data, a style sheet and a font to create a framework. Later on you can add more map data and load extra fonts if necessary. You need:

  • A CTM1 file containing your map.
  • A style sheet: an XML document telling CartoType how to draw the map. This sample code loads CartoType's OSM style sheet, designed for displaying OpenStreetMap data
  • A font. You can load any TrueType, OpenType or PostScript Type 1 font. This sample code loads Deja Vu Sans from the Deja Vu family.

   // declaration of the CFramework object in your application class
   std::unique_ptr<CartoType::CFramework> iFramework;
   // desired width and height of your map in pixels (example values)
   int view_width = 800;
   int view_height = 600;
   // code in your application class's initialization function
   CartoType::TResult error = 0;
   iFramework = CartoType::CFramework::New(error,
      "/CartoType/src/test/data/ctm1/santa-cruz.ctm1",
      "/CartoType/src/style/osm-style.xml",
      "/CartoType/src/font/DejaVuSans.ttf",
      view_width,view_height);

Optionally load some more fonts

You have loaded one font, but it's a good idea to load some more so that the maps look more attractive. The standard style sheet uses more than one typeface. You can load any TrueType, OpenType or PostScript Type 1 fonts you like. In this example some more fonts are loaded from the popular and versatile Deja Vu family. The other is Droid Sans Fallback, which provides Chinese and other scripts not supported by the Deja Vu fonts.

   if (!error)
      error = iFramework ->LoadFont("/CartoType/src/font/DejaVuSans-Bold.ttf");
   if (!error)
      error = iFramework ->LoadFont("/CartoType/src/font/DejaVuSerif.ttf");
   if (!error)
      error = iFramework ->LoadFont("/CartoType/src/font/DejaVuSerif-Italic.ttf");
   if (!error)
      error = iEngine->LoadFont("/CartoType/src/font/DroidSansFallback.ttf");

In this example, for simplicity, the fonts are taken from their standard location in the CartoType sources. In production code you would need to obtain a path to the fonts from somewhere or create it relative to the application's path.

Drawing the map

In these steps you draw the map and handle user interface events, allowing the user to pan, zoom and rotate the map. This functionality happens in your application framework's event handling system, which differs from one platform to another.

Your application framework will have a place where you need to write code to draw into the window or view. For example, in Microsoft Foundation Classes for Windows the function is CView::OnDraw, which you override in your view class. CartoType draws its map into a bitmap owned by the framework object you created earlier (iFramework).

Get the map image

You can get a pointer to the CartoType::TBitmap object holding the map image like this:

CartoType::TResult error = 0;
const CartoType::TBitmap* map_bitmap = iFramework->MapBitmap(error);

Copy the map image to the display

Now you need a pointer to the bitmap's data, which is premultiplied 32-bit ARGB:

 unsigned char* bitmap_data = (unsigned char*)(map_bitmap->Data());

The actual code you use to copy the bitmap to the display is platform-specific:

Windows

This code is taken from the OnDraw function in the Windows demo. It contains references to some variables not discussed above which store clipping rectangles and offsets used when dragging the map to a new position.

 // An extended bitmap info class with a three-entry colour table for bitfield specifiers.
 class BitmapInfoX
   {
   public:
   BITMAPINFOHEADER bmiHeader;
   DWORD bmiColors[3];
   };

 // Blit the drawing surface to the window.
 BitmapInfoX bm;
bm.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
bm.bmiHeader.biWidth = map_bitmap->Width();
bm.bmiHeader.biHeight = -map_bitmap->Height();
 bm.bmiHeader.biPlanes = 1;
 bm.bmiHeader.biBitCount = 32;
 bm.bmiHeader.biCompression = BI_BITFIELDS;
 bm.bmiHeader.biSizeImage = 0;
 bm.bmiHeader.biXPelsPerMeter = 1000;
 bm.bmiHeader.biYPelsPerMeter = 1000;
 bm.bmiHeader.biClrUsed = 0;
 bm.bmiHeader.biClrImportant = 0;
 bm.bmiColors[0] = 0xFF000000;
 bm.bmiColors[1] = 0xFF0000;
 bm.bmiColors[2] = 0xFF00;
 unsigned char* bitmap_data = (unsigned char*)(map_bitmap->Data());
   /*
   Blit the whole GC if the clip rectangle is at the bottom left of the GC.
   This gets round a bug in StretchDIBits (when dealing with 32bpp bitmaps) that causes a y coordinate of 0 to
   be treated as the top, not the bottom, of the source bitmap. 
   */
   if (source_clip.Top() > 0 && source_clip.Left() == 0 && source_clip.Bottom() == map_bitmap->Height())
      ::StretchDIBits(*pDC,
         iMapDragOffset.iX,iMapDragOffset.iY,map_bitmap->Width(),map_bitmap->Height(),
         0,0,map_bitmap->Width(),map_bitmap->Height(),
         map_data,(BITMAPINFO*)&bm,DIB_RGB_COLORS,SRCCOPY);
      else
         ::StretchDIBits(*pDC,dest_clip.Left(),dest_clip.Top(),dest_clip.Width(),dest_clip.Height(),
         source_clip.Left(),map_bitmap->Height() - source_clip.Bottom(),source_clip.Width(),source_clip.Height(),
          map_data,(BITMAPINFO*)&bm,DIB_RGB_COLORS,SRCCOPY);

iOS

This code is adapted from CartoTypeModel.mm, an Objective C++ source file used in one of the iOS demo programs.

// A function to create a bitmap context wrapping a bitmap from a CartoType 32-bit graphics context.
static CGContextRef MyCreateBitmapContext(int aWidth,int aHeight,void* aData)
   {
   int row_bytes = aWidth * 4;
   CGColorSpaceRef color_space = CGColorSpaceCreateDeviceRGB();
   CGContextRef context = CGBitmapContextCreate(aData,aWidth,aHeight,8,
      row_bytes,color_space,
      kCGBitmapByteOrder32Little | kCGImageAlphaPremultipliedLast);
   CGColorSpaceRelease(color_space);
   return context;
 }
/*
The following code extract creates a CGImage object called 'image'. This image can then be drawn to
a CGContextRef using CGContextDrawImage.
*/
   if (!error)
      {
      CGContextRef cr = MyCreateBitmapContext(map_bitmap->Width(),map_bitmap->Height(),
      map_bitmap->Data()); 
      image = CGBitmapContextCreateImage(cr); 
      CGContextRelease(cr);
      }