Hello everyone,
I’ve been using the modestMaps extension for Open Frameworks, but had trouble with the program crashing while trying to load tiles. After trying to find the problem, I decided to write my own, very basic code to load the tiles so I could simplify everything and hopefully figure out the problem. The software has to run in a public setting, so any crashes while it’s being used would be unpleasant.
For testing purposes, I’m having the map zoom in and out and move around automatically. It runs fine for about 20-40 minutes, then crashes with a EXC_BAD_ACCESS.
If I’m able to solve these crashes, I’ll try to publish my code in a neatly packaged add-on, so other people can use a well running map for their applications as well.
Here is the code I’m using to load and draw images, as well as a screenshot of the crash I’m getting.
I’ve tried the thread both with blocking on and blocking off, both seem to crash with the same errors.
Any help would be greatly appreciated.
hvMap.cpp
hvMap::hvMap() {
imageLoader.startThread(true, false);
}
hvMap::~hvMap() {
imageLoader.stopThread();
}
void hvMap::update() {
//figure out which tiles to load
int baseZoom = bestZoomForScale((float)sc);
hvCoordinate startCoord = pointCoordinate(hvPixel(0,0)).zoomTo(baseZoom).container();
hvCoordinate endCoord = pointCoordinate(hvPixel(width,height)).zoomTo(baseZoom).container().right().down();
int minCol = startCoord.column;
int maxCol = endCoord.column;
int minRow = startCoord.row;
int maxRow = endCoord.row;
minCol -= GRID_PADDING;
minRow -= GRID_PADDING;
maxCol += GRID_PADDING;
maxRow += GRID_PADDING;
//ask the loader to load the tiles (loader checks if they are already loaded)
for(int column = minCol; column <= maxCol; column++) {
for(int row = minRow; row <= maxRow; row++) {
hvCoordinate coordinate = hvCoordinate(row, column, baseZoom);
loadTile(coordinate);
}
}
//if the image is loaded, turn on the texture and update so we can draw the image
if(imageLoader.lock()) {
for(map<hvCoordinate, ofImage>::iterator it = imageLoader.loadedFiles.begin(); it != imageLoader.loadedFiles.end(); it++) {
if(it->second.getWidth() > 0 && !it->second.isUsingTexture()) {
it->second.setUseTexture(true);
it->second.update();
}
}
imageLoader.unlock();
}
//put all images outside of the screen and from other zoom levels in a vector to remove
//TO DO: draw bigger images first, if not all 4 smaller images are loaded.
remove.clear();
if(imageLoader.lock()) {
for(map<hvCoordinate, ofImage>::iterator it = imageLoader.loadedFiles.begin(); it != imageLoader.loadedFiles.end(); it++) {
bool removing = false;
if(
it->first.column < minCol ||
it->first.column > maxCol ||
it->first.row < minRow ||
it->first.row > maxRow
) {
remove.push_back(hvCoordinate(it->first));
removing = true;
}
if(!removing && it->first.zoom != baseZoom) {
remove.push_back(hvCoordinate(it->first));
}
}
imageLoader.unlock();
}
//remove the unneeded images
for(int i = 0; i < remove.size(); i++) {
removeTile(remove.at(i));
}
}
void hvMap::draw() {
//scale and move the tiles accordingly
double correction = 1.0 / pow(2.0, bestZoomForScale((float)sc));
ofPushMatrix();
ofTranslate(width / 2.0, height / 2.0);
ofScale(sc, sc, 1);
ofTranslate(tx, ty);
ofScale(correction, correction, 1);
//draw the tiles
if(imageLoader.lock()) {
for(map<hvCoordinate, ofImage>::iterator it = imageLoader.loadedFiles.begin(); it != imageLoader.loadedFiles.end(); it++) {
if(it->second.isAllocated() && it->second.isUsingTexture()) {
it->second.draw(it->first.column * TILE_SIZE, it->first.row * TILE_SIZE, TILE_SIZE, TILE_SIZE);
}
}
imageLoader.unlock();
}
ofPopMatrix();
}
void hvMap::loadTile(hvCoordinate coordinate) {
if (coordinate.row >= 0 && coordinate.row < pow(2, coordinate.zoom)) {
hvCoordinate c = sourceCoordinate(coordinate);
string url = string("~/tiles/");
url.append(ofToString((int)c.zoom));
url.append("/");
url.append(ofToString((int)c.column));
url.append("/");
url.append(ofToString((int)c.row));
url.append(".png");
imageLoader.loadCoordinate(coordinate, url);
}
}
void hvMap::removeTile(hvCoordinate coordinate) {
imageLoader.deleteCoordinate(coordinate);
}
hvImageLoader.h
#include "ofMain.h"
#include "hvCoordinate.h"
class hvImageLoader : public ofThread {
public:
hvImageLoader();
~hvImageLoader();
void setup();
void update();
void threadedFunction();
void loadCoordinate(hvCoordinate coordinate, string url);
void deleteCoordinate(hvCoordinate coordinate);
map<hvCoordinate, string> filenames;
map<hvCoordinate, ofImage> loadedFiles;
};
hvImageLoader.cpp
void hvImageLoader::threadedFunction() {
while(isThreadRunning()) {
while(filenames.size() > 0) {
ofImage image;
image.setUseTexture(false);
image.loadImage(ofFile(filenames.begin()->second));
if(lock()) {
loadedFiles[filenames.begin()->first] = image;
filenames.erase(filenames.begin());
unlock();
} else {
cout << "hvImageLoader::threadedFunction() --> lock() == false" << endl;
}
}
}
}
void hvImageLoader::loadCoordinate(hvCoordinate coordinate, string url) {
if(lock()) {
if(
filenames.count(coordinate) == 0 &&
loadedFiles.count(coordinate) == 0
) {
filenames[coordinate] = url;
}
unlock();
} else {
cout << "hvImageLoader::loadCoordinate() --> lock() == false" << endl;
}
}
void hvImageLoader::deleteCoordinate(hvCoordinate coordinate) {
if(lock()) {
if(loadedFiles.count(coordinate) > 0) {
//if(loadedFiles[coordinate].isUsingTexture()) {
loadedFiles.erase(loadedFiles.find(coordinate));
//}
}
unlock();
} else {
cout << "hvImageLoader::deleteCoordinate() --> lock() == false" << endl;
}
}