public class GeoARApplication extends Application {
	private static Logger LOG;

	public static Context applicationContext;

	public static final String PREFERENCES_FILE = "GeoAR";
	private static final String STACKTRACE_FILENAME = "stacktrace.log";
	private static final String LOG_FILEPATH = "logs/logfile.txt";
	private static final String REPORT_EMAIL = "geoar@52north.org";

	private UncaughtExceptionHandler defaultUncaughtExceptionHandler;

	private static File stacktraceFile;
	private static File logFile;

	@Override
	public void onCreate() {
		// Store application Context as static field
		applicationContext = getApplicationContext();

		// File to save uncaught exception trace to
		stacktraceFile = new File(applicationContext.getFilesDir(),
				STACKTRACE_FILENAME);
		// File path to logfile
		logFile = new File(applicationContext.getFilesDir() + "/"
				+ LOG_FILEPATH);
		// Set system properties with log file, logback config references to
		// these,
		// so do not use LoggerFactory before this point
		System.setProperty("LOG_DIR", logFile.getParent());
		int sepIndex = logFile.getName().lastIndexOf(".");
		System.setProperty("LOG_NAME", logFile.getName().substring(0, sepIndex));
		System.setProperty("LOG_EXT", logFile.getName().substring(sepIndex + 1));

		LOG = LoggerFactory.getLogger(GeoARApplication.class);

		// Set uncaught exception handler
		defaultUncaughtExceptionHandler = Thread
				.getDefaultUncaughtExceptionHandler();
		Thread.setDefaultUncaughtExceptionHandler(new UncaughtExceptionHandler() {

			@Override
			public void uncaughtException(Thread thread, Throwable ex) {
				LOG.error("Uncaught exception in thread " + thread.getName(),
						ex);
				try {
					FileOutputStream fos = openFileOutput(STACKTRACE_FILENAME,
							Context.MODE_PRIVATE);
					ex.printStackTrace(new PrintStream(fos));
					fos.close();
				} catch (FileNotFoundException e) {
					e.printStackTrace();
				} catch (IOException e) {
					e.printStackTrace();
				}

				// fall back to standard handler
				if (defaultUncaughtExceptionHandler != null) {
					defaultUncaughtExceptionHandler.uncaughtException(thread,
							ex);
				}
			}
		});

		// ensure that plugins can access the main logger
		DataSourceLoggerFactory.setLoggerCallable(new LoggerCallable() {
			@Override
			public org.n52.geoar.utils.DataSourceLoggerFactory.Logger call(
					Class<?> clazz) {
				return new PluginLogger(clazz);
			}
		});

		super.onCreate();
	}

	/**
	 * Checks if there is a recorded stacktrace
	 */
	public static boolean checkAppFailed() {
		return stacktraceFile.exists();
	}

	/**
	 * Deletes latest stacktrace record
	 */
	public static void clearAppFailed() {
		if (!checkAppFailed())
			return;

		new File(applicationContext.getFilesDir(), STACKTRACE_FILENAME)
				.delete();
	}

	/**
	 * Creates an {@link Intent#ACTION_SEND_MULTIPLE} Intent to create an email
	 * with last stacktrace and current logfiles as attachments.
	 * 
	 * @param context
	 */
	public static void sendFailMail(Activity context) {
		List<File> attachments = new ArrayList<File>();
		if (checkAppFailed()) {
			attachments.add(stacktraceFile);
		}

		if (logFile.exists()) {
			attachments.add(logFile);
		}
		sendMail(context, "GeoAR Error Report",
				applicationContext.getString(R.string.text_error_report),
				attachments);
	}

	/**
	 * Copies specified {@link File}s to globally readable locations and returns
	 * their {@link Uri}s
	 * 
	 * @param attachments
	 * @return
	 * @throws IOException
	 */
	private static ArrayList<Uri> getFailAttachments(List<File> attachments)
			throws IOException {
		ArrayList<Uri> copiesUris = new ArrayList<Uri>(attachments.size());
		for (int i = 0, attachmentsSize = attachments.size(); i < attachmentsSize; i++) {

			InputStream inputStream = new FileInputStream(attachments.get(i));
			int sepIndex = attachments.get(i).getName().lastIndexOf(".");
			attachments.get(i).getName().substring(sepIndex);

			File fileCopy = new File(
					applicationContext.getExternalFilesDir(null), "fail_"
							+ i
							+ attachments.get(i).getName()
									.substring(sepIndex + 1));
			copiesUris.add(Uri.fromFile(fileCopy));
			OutputStream outputStream = new FileOutputStream(fileCopy);

			byte[] buf = new byte[4096];
			int len;
			while ((len = inputStream.read(buf)) > 0) {
				outputStream.write(buf, 0, len);
			}
			inputStream.close();
			outputStream.close();
		}

		return copiesUris;
	}

	public static void sendFeedbackMail(Activity context) {
		List<File> attachments = new ArrayList<File>();
		if (logFile.exists()) {
			attachments.add(logFile);
		}
		sendMail(context, "GeoAR Feedback Report",
				applicationContext.getString(R.string.text_feedback_report),
				attachments);
	}

	private static void sendMail(Activity context, String subject,
			String message, List<File> attachments) {
		Intent intent = new Intent(Intent.ACTION_SEND_MULTIPLE);
		intent.setType("text/plain");
		intent.putExtra(Intent.EXTRA_SUBJECT, subject);
		intent.putExtra(Intent.EXTRA_TEXT, message);
		intent.putExtra(Intent.EXTRA_EMAIL, new String[] { REPORT_EMAIL });

		try {
			intent.putParcelableArrayListExtra(Intent.EXTRA_STREAM,
					getFailAttachments(attachments));
			context.startActivity(intent);
		} catch (ActivityNotFoundException e) {
			Toast.makeText(context, R.string.could_not_find_email_application,
					Toast.LENGTH_LONG).show();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
}
 public interface Cancelable {
    void cancel();
}
 public enum DataSourceErrorType {
    UNKNOWN, CONNECTION, CANCELED
}
 public abstract interface GetDataBoundsCallback extends
        OnProgressUpdateListener {
    void onReceiveDataUpdate(MercatorRect bbox,
            List<? extends SpatialEntity2<? extends Geometry>> data);

    void onAbort(MercatorRect bbox, DataSourceErrorType reason);
}
 public class DataSourceInstanceHolder implements Parcelable {

	public interface DataSourceSettingsChangedListener {
		void onDataSourceSettingsChanged();
	}

	private static int nextId = 0;
	private static final int CLEAR_CACHE = 1;
	private static final int CLEAR_CACHE_AFTER_DEACTIVATION_DELAY = 10000;
	private static final Logger LOG = LoggerFactory
			.getLogger(DataSourceInstanceHolder.class);

	private Handler dataSourceHandler = new Handler(new Handler.Callback() {
		@Override
		public boolean handleMessage(Message msg) {
			if (msg.what == CLEAR_CACHE) {
				// Delayed clearing of cache after datasource has been
				// deactivated
				dataCache.clearCache();
				return true;
			}

			return false;
		}
	});
	private DataSource<? super Filter> dataSource;
	private boolean injected = false;
	private DataSourceHolder parentHolder;
	private final int id = nextId++;
	private DataCache dataCache;
	private Filter currentFilter;
	private Boolean hasSettings = null;
	@CheckManager
	private CheckList<DataSourceInstanceHolder>.Checker mChecker;
	private Exception lastError;
	private Set<DataSourceSettingsChangedListener> mSettingsChangedListeners = new HashSet<DataSourceInstanceHolder.DataSourceSettingsChangedListener>(
			0);

	public DataSourceInstanceHolder(DataSourceHolder parentHolder,
			DataSource<? super Filter> dataSource) {
		this.parentHolder = parentHolder;
		this.dataSource = dataSource;
		try {
			this.currentFilter = parentHolder.getFilterClass().newInstance();
		} catch (InstantiationException e) {
			throw new RuntimeException(
					"Referenced filter has no appropriate constructor");
		} catch (IllegalAccessException e) {
			throw new RuntimeException(
					"Referenced filter has no appropriate constructor");
		}

		dataCache = new DataCache(this);
	}

	@CheckedChangedListener
	public void checkedChanged(boolean state) {
		if (state) {
			activate();
		} else {
			deactivate();
		}
	}

	/**
	 * Prevents datasource from getting unloaded. Should be called when
	 * datasource is added to map/ar.
	 */
	public void activate() {
		LOG.info("Activating data source instance " + getName());

		// prevents clearing of cache by removing messages
		dataSourceHandler.removeMessages(CLEAR_CACHE);
		if (!injected) {
			parentHolder.perfomInjection(dataSource);
			injected = true;
		}
	}

	/**
	 * Queues unloading of datasource and cached data
	 */
	public void deactivate() {
		// Clears the cache 30s after calling this method
		LOG.info("Deactivating data source " + getName());
		dataSourceHandler.sendMessageDelayed(
				dataSourceHandler.obtainMessage(CLEAR_CACHE),
				CLEAR_CACHE_AFTER_DEACTIVATION_DELAY);
	}

	public DataCache getDataCache() {
		return dataCache;
	}

	public void createSettingsDialog(Context context) {
		SettingsResultListener resultListener = new SettingsResultListener() {
			@Override
			void onSettingsResult(int resultCode) {
				if (resultCode == Activity.RESULT_OK) {
					notifySettingsChanged();
				}
			}
		};

		Intent intent = new Intent(context,
				DataSourceInstanceSettingsDialogActivity.class);
		intent.putExtra("dataSourceInstance", this);
		intent.putExtra("resultListener", resultListener); // unsure whether
															// Intent uses weak
															// references too
		context.startActivity(intent);
	}

	public Filter getCurrentFilter() {
		return currentFilter;
	}

	public DataSourceHolder getParent() {
		return parentHolder;
	}

	public void addOnSettingsChangedListener(
			DataSourceSettingsChangedListener listener) {
		mSettingsChangedListeners.add(listener);
	}

	public void removeOnSettingsChangedListener(
			DataSourceSettingsChangedListener listener) {
		mSettingsChangedListeners.remove(listener);
	}

	/**
	 * It does not only notify listeners, but also clears the current cache.
	 * TODO
	 */
	void notifySettingsChanged() {
		dataCache.setFilter(currentFilter);
		for (DataSourceSettingsChangedListener listener : mSettingsChangedListeners) {
			listener.onDataSourceSettingsChanged();
		}
	}

	public String getName() {
		if (parentHolder.getNameCallbackMethod() != null) {
			// try to use name callback
			try {
				return (String) parentHolder.getNameCallbackMethod().invoke(
						dataSource);
			} catch (Exception e) {
				LOG.warn("Data Source " + parentHolder.getName()
						+ " NameCallback fails");
			}
		}

		if (parentHolder.instanceable()) {
			return parentHolder.getName() + " " + id;
		} else {
			return parentHolder.getName();
		}
	}

	public DataSource<? super Filter> getDataSource() {
		return dataSource;
	}

	public boolean hasSettings() {
		if (hasSettings == null) {
			hasSettings = SettingsHelper.hasSettings(getCurrentFilter())
					|| SettingsHelper.hasSettings(getDataSource());
		}

		return hasSettings;
	}

	@Override
	public int describeContents() {
		return 0;
	}

	@Override
	public void writeToParcel(Parcel dest, int flags) {
		// Parcel based on unique DataSourceHolder id
		dest.writeParcelable(parentHolder, 0);
		dest.writeInt(id);
	}

	public static final Parcelable.Creator<DataSourceInstanceHolder> CREATOR = new Parcelable.Creator<DataSourceInstanceHolder>() {
		public DataSourceInstanceHolder createFromParcel(Parcel in) {
			DataSourceHolder dataSource = in
					.readParcelable(DataSourceHolder.class.getClassLoader());
			int id = in.readInt();
			// Find DataSourceInstance with provided id
			for (DataSourceInstanceHolder instance : dataSource.getInstances()) {
				if (instance.id == id) {
					return instance;
				}
			}

			return null;
		}

		public DataSourceInstanceHolder[] newArray(int size) {
			return new DataSourceInstanceHolder[size];
		}
	};

	public boolean isChecked() {
		return mChecker.isChecked();
	}

	public void setChecked(boolean state) {
		mChecker.setChecked(state);
	}

	public void saveState(ObjectOutputStream objectOutputStream)
			throws IOException {

		// Store filter, serializable
		objectOutputStream.writeObject(currentFilter);

		// Store data source instance settings using settings framework
		SettingsHelper.storeSettings(objectOutputStream, this.dataSource);

		objectOutputStream.writeBoolean(isChecked());
	}

	public void restoreState(ObjectInputStream objectInputStream)
			throws IOException {
		try {
			// restore filter, serializable
			currentFilter = (Filter) objectInputStream.readObject();
			dataCache.setFilter(currentFilter);
		} catch (ClassNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

		// restore data source instance settings, settings framework
		SettingsHelper.restoreSettings(objectInputStream, this.dataSource);

		setChecked(objectInputStream.readBoolean());
	}

	public void reportError(Exception e) {
		lastError = e;
	}

	public void clearError() {
		lastError = null;
	}

	public String getErrorString() {
		if (lastError == null) {
			return null;
		}

		if (lastError instanceof SocketException) {
			return GeoARApplication.applicationContext
					.getString(R.string.connection_error);
		} else if (lastError instanceof PluginException) {
			return ((PluginException) lastError).getTitle();
		}

		return GeoARApplication.applicationContext
				.getString(R.string.unknown_error);
	}

	public boolean hasErrorMessage() {
		if (lastError == null) {
			return false;
		}
		return lastError instanceof PluginException;
	}

	public String getErrorMessage() {
		if (lastError == null || !(lastError instanceof PluginException)) {
			return null;
		}

		return lastError.toString();
	}
}
 public interface DataSourceSettingsChangedListener {
	void onDataSourceSettingsChanged();
}
 public class InfoView extends LinearLayout {

	private static final int MESSAGE_REFRESH = 1;

	private static class ProgressHolder {
		private int progress, maxProgress;
	}

	private static class StatusHolder {
		private long clearTime;
		private String status = "";
		private Object id;
	}

	private interface OnChangeListener {
		void onChange();
	}

	private static Handler updatehandler = new Handler() {
		@Override
		public void handleMessage(Message msg) {
			if (msg.what == MESSAGE_REFRESH) {
				notifyStatusChangeListeners();
			}
		}
	};

	private OnChangeListener statusChangeListener = new OnChangeListener() {
		@Override
		public void onChange() {
			statusUpdate();
		}
	};

	private OnChangeListener progressChangeListener = new OnChangeListener() {
		@Override
		public void onChange() {
			progressUpdate();
		}
	};

	private ProgressBar infoProgressBar;

	// HashMaps of all identifiers and their information to show
	private static Map<Object, ProgressHolder> progressHolderMap = new HashMap<Object, ProgressHolder>();
	private static LinkedHashMap<Object, StatusHolder> statusHolderMap = new LinkedHashMap<Object, StatusHolder>(
			0, 1, true);
	private static Set<OnChangeListener> statusChangeListeners = new HashSet<InfoView.OnChangeListener>();
	private static Set<OnChangeListener> progressChangeListeners = new HashSet<InfoView.OnChangeListener>();

	private StatusHolder currentStatus;
	private ProgressHolder currentProgress = new ProgressHolder();

	private TextView infoStatusTextView;
	private Runnable updateViewsRunnable = new Runnable() {
		public void run() {
			updateRunnableQueued = false;
			updateViews();
		}
	};
	private boolean updateRunnableQueued;

	public InfoView(Context context, AttributeSet attrs) {
		super(context, attrs);
		setOrientation(VERTICAL);

		// inflate layout
		LayoutInflater.from(context).inflate(R.layout.infoviews, this, true);

		infoStatusTextView = (TextView) findViewById(R.id.textViewStatusInfo);
		infoProgressBar = (ProgressBar) findViewById(R.id.progressBar);

		statusChangeListeners.add(statusChangeListener);
		progressChangeListeners.add(progressChangeListener);

		Timer timer = new Timer(true);
		timer.scheduleAtFixedRate(new TimerTask() {
			@Override
			public void run() {
				statusUpdate();
			}
		}, 0, 3000);

		statusUpdate();
		updateViews();
	}

	private void statusUpdate() {
		synchronized (statusHolderMap) {
			if (statusHolderMap.size() != 0) {
				// get next status to show
				Iterator<StatusHolder> iterator = statusHolderMap.values()
						.iterator();
				long now = SystemClock.uptimeMillis();
				currentStatus = null;
				// try to get the very first one which is not outdated
				while (iterator.hasNext()) {
					StatusHolder holder = iterator.next();
					if (holder.clearTime <= now) {
						iterator.remove();
					} else {
						currentStatus = holder;
						break;
					}
				}
				if (currentStatus != null) {
					// just get the object which we already have, to
					// update the internal access order so that that
					// element gets biased against the others.
					statusHolderMap.get(currentStatus.id);
				}
				refresh();
			} else if (currentStatus != null) {
				currentStatus = null;
			}
		}
	}

	private void progressUpdate() {
		synchronized (progressHolderMap) {
			currentProgress.progress = 0;
			currentProgress.maxProgress = 0;
			for (ProgressHolder holder : progressHolderMap.values()) {
				currentProgress.progress += holder.progress;
				currentProgress.maxProgress += holder.maxProgress;
			}

			if (currentProgress.progress >= currentProgress.maxProgress) {
				for (ProgressHolder holder : progressHolderMap.values()) {
					clearStatus(holder);
				}
				progressHolderMap.clear();
			}
			refresh();
		}
	}

	private static void notifyStatusChangeListeners() {
		for (OnChangeListener listener : statusChangeListeners)
			listener.onChange();
	}

	private static void notifyProgressChangeListeners() {
		for (OnChangeListener listener : progressChangeListeners)
			listener.onChange();
	}

	/**
	 * Allows to update views state from any thread
	 */
	private void refresh() {
		if (!updateRunnableQueued) {
			updateRunnableQueued = true;
			post(updateViewsRunnable);
		}
	}

	/**
	 * Set the specific status for the identification object
	 * 
	 * @param progress
	 * @param maxProgress
	 * @param id
	 */
	public static void setProgress(int progress, int maxProgress, Object id) {
		synchronized (progressHolderMap) {

			ProgressHolder holder = progressHolderMap.get(id);
			if (holder == null) {
				holder = new ProgressHolder();
				progressHolderMap.put(id, holder);
			}
			holder.maxProgress = maxProgress;
			holder.progress = progress;
			if (holder.progress >= holder.maxProgress) {
				clearStatus(holder);
			}
		}
		notifyProgressChangeListeners();
	}

	/**
	 * sets a progress title for this id
	 * 
	 * @param title
	 * @param id
	 */
	public static void setProgressTitle(String title, Object id) {
		synchronized (progressHolderMap) {
			ProgressHolder holder = progressHolderMap.get(id);
			if (holder == null) {
				holder = new ProgressHolder();
				progressHolderMap.put(id, holder);
			}
			setStatus(title, -1, holder);
		}
	}

	public static void setProgressTitle(int stringId, Object id) {
		setProgressTitle(
				GeoARApplication.applicationContext.getString(stringId), id);
	}

	/**
	 * Sets status text for id
	 * 
	 * @param stringId
	 * @param maxDuration
	 * @param id
	 */
	public static void setStatus(int stringId, int maxDuration, Object id) {
		setStatus(GeoARApplication.applicationContext.getString(stringId),
				maxDuration, id);
	}

	/**
	 * Sets status text for id
	 * 
	 * @param status
	 * @param maxDuration
	 * @param id
	 */
	public static void setStatus(String status, int maxDuration, Object id) {
		boolean update = false;
		synchronized (statusHolderMap) {
			StatusHolder holder = statusHolderMap.get(id);
			if (holder == null) {
				holder = new StatusHolder();
				holder.id = id;
				statusHolderMap.put(id, holder);
				update = true;
			}
			if (holder.status != null && !holder.status.equals(status)) {
				update = true;
			}
			holder.status = status;
			if (maxDuration != -1) {
				holder.clearTime = SystemClock.uptimeMillis() + maxDuration;
			} else {
				holder.clearTime = Long.MAX_VALUE;
			}
			updatehandler.sendMessageAtTime(
					updatehandler.obtainMessage(MESSAGE_REFRESH),
					holder.clearTime);
		}

		if (update) {
			notifyStatusChangeListeners();
		}
	}

	/**
	 * Clears status text for id
	 * 
	 * @param id
	 */
	public static void clearStatus(Object id) {
		synchronized (statusHolderMap) {
			statusHolderMap.remove(id);
		}
		notifyStatusChangeListeners();
	}

	/**
	 * Updates views to reflect current status and progress
	 */
	private void updateViews() {
		boolean isVisible = false;
		synchronized (progressHolderMap) {
			if (currentProgress.progress < currentProgress.maxProgress) {
				infoProgressBar.setMax(currentProgress.maxProgress);
				infoProgressBar.setProgress(currentProgress.progress);

				infoProgressBar.setVisibility(View.VISIBLE);
				isVisible = true;
			} else {
				infoProgressBar.setVisibility(View.GONE);
			}
		}
		synchronized (statusHolderMap) {
			if (currentStatus != null) {
				infoStatusTextView.setText(currentStatus.status);
				infoStatusTextView.setVisibility(View.VISIBLE);
				isVisible = true;
			} else {
				infoStatusTextView.setVisibility(View.GONE);
			}
		}

		if (isVisible) {
			setVisibility(View.VISIBLE);
		} else {
			setVisibility(View.GONE);
		}
	}

	/**
	 * Clears progress by id
	 * 
	 * @param id
	 */
	public static void clearProgress(Object id) {
		setProgress(0, 0, id);
	}

} public class Settings {

	// AR view settings
	// Radius in meters for the interpolation in AR view
	public static final int SIZE_AR_INTERPOLATION = 1500;
	// Threshold in position to request new interpolation
	public static final int RELOAD_DIST_AR = 50;
	// Zoom used in AR view for interpolation, please make sure it fits somehow
	// with SIZE_AR_INTERPOLATION...
	public static final byte ZOOM_AR = 12;

	// Buffer in px to request in advance around map extent
	public static final int BUFFER_MAPINTERPOLATION = 100;

}
 public abstract class RenderFeature2 extends Spatial implements
		DataSourceVisualizationGL, OpenGLCallable, OnInitializeInGLThread {


	/** Static constants */
	protected static final int SIZE_OF_POSITION = 3;
	protected static final int SIZE_OF_NORMAL = 3;
	protected static final int SIZE_OF_COLOR = 4;
	protected static final int SIZE_OF_TEXCOORD = 2;
	
	protected static final int SIZE_OF_FLOAT = 4;
	protected static final int SIZE_OF_INT = 4;
	protected static final int SIZE_OF_SHORT = 2;

	/**
	 * 
	 * @author Arne de Wall
	 * 
	 */
	private abstract class FeatureGeometry {

		protected int verticesCount;
		protected boolean hasNormals;
		protected boolean hasColors;
		protected boolean hasTextureCoords;

		abstract void render();
	}

	/**
	 * 
	 * @author Arne de Wall
	 * 
	 */
	@Deprecated
	private final class FeatureGeometryVBOandIBO extends FeatureGeometry {

		protected class BufferDetails {
			public final Buffer buffer;
			public final short bufferType;
			public final int bufferHandle;
			public final int byteSize;
			public final int target;

			public BufferDetails(final float[] data, final int bufferHandle,
					final int target) {
				final FloatBuffer floatBuffer = ByteBuffer
						.allocateDirect(data.length * SIZE_OF_FLOAT)
						.order(ByteOrder.nativeOrder()).asFloatBuffer();
				floatBuffer.put(data).compact().position(0);
				this.buffer = floatBuffer;
				this.bufferType = FLOAT_BUFFER;
				this.byteSize = SIZE_OF_FLOAT;
				this.target = target;
				this.bufferHandle = bufferHandle;
			}

			@SuppressWarnings("unused")
			public BufferDetails(final int[] data, final int bufferHandle,
					final int target) {
				final IntBuffer integerBuffer = ByteBuffer
						.allocateDirect(data.length * SIZE_OF_INT)
						.order(ByteOrder.nativeOrder()).asIntBuffer();
				integerBuffer.put(data).compact().position(0);
				this.buffer = integerBuffer;
				this.bufferType = INT_BUFFER;
				this.byteSize = SIZE_OF_INT;
				this.target = target;
				this.bufferHandle = bufferHandle;
			}

			public BufferDetails(final short[] data, final int bufferHandle,
					final int target) {
				final ShortBuffer shortBuffer = ByteBuffer
						.allocateDirect(data.length * SIZE_OF_SHORT)
						.order(ByteOrder.nativeOrder()).asShortBuffer();
				shortBuffer.put(data).compact().position(0);
				this.buffer = shortBuffer;
				this.bufferType = SHORT_BUFFER;
				this.byteSize = SIZE_OF_SHORT;
				this.target = target;
				this.bufferHandle = bufferHandle;
			}

			void bindBuffer() {
				GLES20.glBindBuffer(target, bufferHandle);
				GLES20.glBufferData(target, buffer.capacity() * byteSize,
						buffer, GLES20.GL_STATIC_DRAW);
				GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, 0);
			}

		}

		static final short FLOAT_BUFFER = 100;
		static final short INT_BUFFER = 101;
		static final short SHORT_BUFFER = 102;

		protected BufferDetails verticesDetails;
		protected BufferDetails colorsDetails;
		protected BufferDetails normalsDetails;
		protected BufferDetails textureDetails;
		protected BufferDetails indicesDetails;

		protected FeatureGeometryVBOandIBO(final float[] vertices,
				final float[] colors, final float[] normals,
				final float[] textureCoords, final short[] indices) {
			if (vertices.length % 3 != 0)
				throw new IllegalArgumentException(
						"[RENDERFEATURE] vertices-array size must be a multiple of three");

			this.hasColors = (colors != null && renderer.supportsColors);
			this.hasNormals = (normals != null && renderer.supportsNormals);
			this.hasTextureCoords = (textureCoords != null && renderer.supportsTextures);

			this.verticesCount = vertices.length / SIZE_OF_POSITION;
			initBuffers(vertices, colors, normals, textureCoords, indices);
		}

		private void initBuffers(final float[] vertices, final float[] colors,
				final float[] normals, final float[] textureCoords,
				final short[] indices) {

			final int bufferCount = 1 + (hasColors ? 1 : 0)
					+ (hasNormals ? 1 : 0) + (hasTextureCoords ? 1 : 0) + 1;

			/** generate buffers on OpenGL */
			final int bufferObjects[] = new int[bufferCount];
			GLES20.glGenBuffers(bufferCount, bufferObjects, 0);
			for (int i = 0; i < bufferCount; i++) {
				if (bufferObjects[i] < 1)
					try {
						throw new Exception(
								"[RENDERFEATURE] initBuffers() -> (Buffer < 1) No Buffer created! ");
					} catch (Exception e) {
						e.printStackTrace();
					}
			}

			int nextBuffer = 0;

			/** generate and bind vertices FloatBuffer */
			verticesDetails = new BufferDetails(vertices,
					bufferObjects[nextBuffer++], GLES20.GL_ARRAY_BUFFER);
			verticesDetails.bindBuffer();

			if (hasColors) {
				/** generate and bind colors FloatBuffer */
				colorsDetails = new BufferDetails(colors,
						bufferObjects[nextBuffer++], GLES20.GL_ARRAY_BUFFER);
				colorsDetails.bindBuffer();
			}

			if (hasNormals) {
				/** generate and bind normals FloatBuffer */
				normalsDetails = new BufferDetails(normals,
						bufferObjects[nextBuffer++], GLES20.GL_ARRAY_BUFFER);
				normalsDetails.bindBuffer();
			}

			if (hasTextureCoords) {
				/** generate and bind texture coordinates FloatBuffer */
				textureDetails = new BufferDetails(textureCoords,
						bufferObjects[nextBuffer++], GLES20.GL_ARRAY_BUFFER);
				textureDetails.bindBuffer();
			}

			indicesDetails = new BufferDetails(indices,
					bufferObjects[nextBuffer++], GLES20.GL_ARRAY_BUFFER);
			indicesDetails.bindBuffer();
		}

		@Override
		void render() {
			/** bind the named vertices buffer object */
			renderer.setVertices(verticesDetails.bufferHandle);
			if (hasColors) {
				/** bind the named color buffer object */
				renderer.setColors(colorsDetails.bufferHandle);
			}
			if (hasNormals) {
				/** bind the named normal buffer object */
				renderer.setNormals(normalsDetails.bufferHandle);
			}
			if (hasTextureCoords) {
				// FIXME NOT IMPLEMENTED YET !
				// renderer.setTextureCoords(textureDetails.bufferHandle);
			}

			/**
			 * bind the named indices buffer object and draw the elements in
			 * respect to the GLES20 drawingMode
			 */
			GLES20.glBindBuffer(GLES20.GL_ELEMENT_ARRAY_BUFFER,
					indicesDetails.bufferHandle);
			GLES20.glDrawElements(drawingMode,
					indicesDetails.buffer.capacity(), GLES20.GL_UNSIGNED_SHORT,
					0);

			GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, 0);
			GLES20.glBindBuffer(GLES20.GL_ELEMENT_ARRAY_BUFFER, 0);
		}

	}

	private final class FeatureGeometryStride extends FeatureGeometry {

		private final FloatBuffer strideBuffer;

		protected FeatureGeometryStride(final float[] vertices,
				final float[] colors, final float[] normals,
				final float[] textureCoords) {
			if (vertices.length % 3 != 0)
				throw new IllegalArgumentException(
						"[RENDERFEATURE] vertices-array size must be a multiple of three");

			this.hasColors = (colors != null && renderer.supportsColors);
			this.hasNormals = (normals != null && renderer.supportsNormals);
			this.hasTextureCoords = (textureCoords != null && renderer.supportsTextures);

			this.verticesCount = vertices.length / SIZE_OF_POSITION;
			this.strideBuffer = initInterleavedBuffer(vertices, colors,
					normals, textureCoords);
		}

		private FloatBuffer initInterleavedBuffer(final float[] vertices,
				final float[] colors, final float[] normals,
				final float[] textureCoords) {

			final int bufferLength = vertices.length
					+ (hasColors ? colors.length : 0)
					+ (hasNormals ? normals.length : 0)
					+ (hasTextureCoords ? textureCoords.length : 0);

			int verticesOffset = 0;
			int normalsOffset = 0;
			int colorsOffset = 0;
			int texturesOffset = 0;

			final FloatBuffer interleavedBuffer = ByteBuffer
					.allocateDirect(bufferLength * SIZE_OF_FLOAT)
					.order(ByteOrder.nativeOrder()).asFloatBuffer();

			for (int i = 0; i < verticesCount; i++) {
				interleavedBuffer.put(vertices, verticesOffset,
						SIZE_OF_POSITION);
				verticesOffset += SIZE_OF_POSITION;
				if (hasColors) {
					interleavedBuffer
							.put(colors, colorsOffset, SIZE_OF_COLOR);
					colorsOffset += SIZE_OF_COLOR;
				}
				if (hasNormals) {
					interleavedBuffer.put(normals, normalsOffset,
							SIZE_OF_NORMAL);
					normalsOffset += SIZE_OF_NORMAL;
				}
				if (hasTextureCoords) {
					interleavedBuffer.put(textureCoords, texturesOffset,
							SIZE_OF_TEXCOORD);
					texturesOffset += SIZE_OF_TEXCOORD;
				}
			}
			interleavedBuffer.position(0);
			return interleavedBuffer;
		}

		@Override
		void render() {
			// @formatter:off
			final int stride = (SIZE_OF_POSITION
					+ (hasColors ? SIZE_OF_COLOR : 0)
					+ (hasNormals ? SIZE_OF_NORMAL : 0) 
					+ (hasTextureCoords ? SIZE_OF_TEXCOORD : 0))
					* SIZE_OF_FLOAT;
			// @formatter:on

			int bufferPosition = 0;
			/** defines the array of generic vertex attribute data */
			strideBuffer.position(bufferPosition);
			final int positionhandle = renderer.getPositionHandle();
			GLES20.glEnableVertexAttribArray(positionhandle);
			GLES20.glVertexAttribPointer(positionhandle, SIZE_OF_POSITION,
					GLES20.GL_FLOAT, false, stride, strideBuffer);
			bufferPosition += SIZE_OF_POSITION;

			if (hasColors) {
				/** defines the array of color attribute data */
				strideBuffer.position(bufferPosition);
				final int colorhandle = renderer.getColorHandle();
				GLES20.glEnableVertexAttribArray(colorhandle);
				GLES20.glVertexAttribPointer(colorhandle, SIZE_OF_COLOR,
						GLES20.GL_FLOAT, false, stride, strideBuffer);
				bufferPosition += SIZE_OF_COLOR;
			}

			if (hasNormals) {
				/** defines the array of vertices normals */
				strideBuffer.position(bufferPosition);
				final int normalhandle = renderer.getNormalHandle();
				GLES20.glEnableVertexAttribArray(normalhandle);
				GLES20.glVertexAttribPointer(normalhandle, SIZE_OF_NORMAL,
						GLES20.GL_FLOAT, false, stride, strideBuffer);
				bufferPosition += SIZE_OF_NORMAL;
			}

			if (hasTextureCoords) {
				// FIXME texture handling here
				strideBuffer.position(bufferPosition);
				final int textureCoordinateHandle = renderer
						.getTextureCoordinateHandle();
				GLES20.glEnableVertexAttribArray(textureCoordinateHandle);
				GLES20.glVertexAttribPointer(textureCoordinateHandle,
						SIZE_OF_TEXCOORD, GLES20.GL_FLOAT, false, stride,
						strideBuffer);
				bufferPosition += SIZE_OF_TEXCOORD;
			}

			/** render primitives from array data */
			GLES20.glDrawArrays(drawingMode, 0, verticesCount);
		}
	}

	/** Feature geometries and shader settings */
	protected FeatureGeometry geometry;
	protected FeatureShader renderer;
	protected BoundingBox boundingBox; // unused

	protected final Stack<OpenGLCallable> childrenFeatures = new Stack<OpenGLCallable>();

	/** Model Matrix of this feature */
	private final float[] modelMatrix = new float[16];
	/** Model-View-Projection Matrix of our feature */
	private final float[] mvpMatrix = new float[16];
	/** temporary Matrix for caching */
	private final float[] tmpMatrix = new float[16];

	/** GL drawing mode - default triangles */
	protected int drawingMode = GLES20.GL_TRIANGLES;
	/** GL for features rendering */
	protected boolean enableBlending = true;
	protected boolean enableDepthTest = true;
	protected boolean enableDepthMask = true;
	protected boolean enableCullFace = false;
	
	protected float heightOffset = 0.0f;
	/** alpha value for Blending */
	protected Float alpha; 
	/** color of the object */
	protected int androidColor; 

	protected boolean isInitialized = false;
	private Texture texture;

	@Deprecated
	protected void setRenderObjectives(float[] vertices, float[] colors,
			float[] normals, float[] textureCoords, short[] indices) {
		if (indices == null || indices.length == 0) {
			setRenderObjectives(vertices, colors, normals, textureCoords);
		} else {

			if (renderer == null) {
				renderer = BilligerColorShader.getInstance();
			}
			// renderer.onCreateInGLESThread();
			geometry = new FeatureGeometryVBOandIBO(vertices, colors, normals,
					textureCoords, indices);
			boundingBox = new BoundingBox(vertices);
		}
	}

	protected void setRenderObjectives(float[] vertices, float[] colors,
			float[] normals, float[] textureCoords) {
		// TODO XXX FIXME not elegant here, maybe this is
		// Jep
		if (renderer == null) {
			if (textureCoords != null && texture != null) {
				renderer = TextureFeatureShader.getInstance();
			} else {
				renderer = BilligerLightShader.getInstance();
			}
		}

		// renderer.onCreateInGLESThread();
		geometry = new FeatureGeometryStride(vertices, colors, normals,
				textureCoords);
		boundingBox = new BoundingBox(vertices);

		isInitialized = true;
	}

	public float[] onScreenCoordsUpdate() {
		if (modelMatrix == null || GLESCamera.projectionMatrix == null
				|| GLESCamera.viewportMatrix == null) {
			return null;
		}
		float[] output = new float[3];
		int res = GLU.gluProject(position[0], position[1], position[2],
				modelMatrix, 0, GLESCamera.projectionMatrix, 0,
				GLESCamera.viewportMatrix, 0, output, 0);

		if (res == GL10.GL_FALSE)
			return null;
		return output;
	}

	public void transform() {
		// TODO
		// gl.glTranslatef(tx, ty, tz);
		// gl.glRotatef(rz, 0, 0, 1);
		// gl.glRotatef(ry, 0, 1, 0);
		// gl.glRotatef(rx, 1, 0, 0);
		// gl.glScalef(sx, sy, sz);
	}

	@Override
	public void render(final float[] projectionMatrix,
			final float[] viewMatrix, final float[] parentMatrix,
			final float[] lightPosition) {
		if (!isInitialized)
			return;
		
		/** set the matrices to identity matrix */
		Matrix.setIdentityM(modelMatrix, 0);
		Matrix.setIdentityM(mvpMatrix, 0);
		Matrix.setIdentityM(tmpMatrix, 0);
		/** translate feature to the position relative to the device */
		// TODO i think position[0] must be translated negatively -> Check
		Matrix.translateM(modelMatrix, 0, position[0], position[1], position[2]);

		if (parentMatrix != null) {
			Matrix.multiplyMM(tmpMatrix, 0, parentMatrix, 0, modelMatrix, 0);
			System.arraycopy(tmpMatrix, 0, modelMatrix, 0, 16);
			Matrix.setIdentityM(tmpMatrix, 0);
		}

		Matrix.multiplyMM(modelMatrix, 0, viewMatrix, 0, modelMatrix, 0);
		Matrix.multiplyMM(mvpMatrix, 0, projectionMatrix, 0, modelMatrix, 0);

		render(mvpMatrix, modelMatrix, lightPosition);
		
		for(OpenGLCallable childFeature : childrenFeatures){
			childFeature.render(projectionMatrix, viewMatrix, modelMatrix, lightPosition);
		}
	}

	public void render(float[] mvpMatrix) {
		/** sets the program object as part of current rendering state */
		if (!isInitialized)
			return;
		renderer.useProgram();
		
		if(enableBlending){
			GLES20.glEnable(GLES20.GL_BLEND);
		}
		if(enableCullFace){
			GLES20.glEnable(GLES20.GL_CULL_FACE);
		}

		if (texture != null) {
			// Set the active texture unit to texture unit 0.
			GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
			GLES20.glUniform1i(renderer.getTextureUniform(), 0);

			// // Bind the texture to this unit.
			// GLES20.glBindTexture(GLES20.GL_TEXTURE_2D,
			// textureDetails.textureId);

			texture.bindTexture();
		}
		renderer.setModelViewProjectionMatrix(mvpMatrix);
		
		/** render the geometry of this feature */
		if (geometry != null)
			geometry.render();
	}
	

	public void render(float[] mvpMatrix, float[] mvMatrix,
			float[] lightPosition) {
		/** sets the program object as part of current rendering state */
		renderer.useProgram();
		
		if(enableBlending){
			GLES20.glEnable(GLES20.GL_BLEND);
		}
		if(enableCullFace){
			GLES20.glEnable(GLES20.GL_CULL_FACE);
		}

		renderer.setLightPositionVec(lightPosition);
		
		if (texture != null) {
			// Set the active texture unit to texture unit 0.
			GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
			GLES20.glUniform1i(renderer.getTextureUniform(), 0);

			// // Bind the texture to this unit.
			// GLES20.glBindTexture(GLES20.GL_TEXTURE_2D,
			// textureDetails.textureId);

			texture.bindTexture();
		}

		renderer.setModelViewMatrix(mvMatrix);
		renderer.setModelViewProjectionMatrix(mvpMatrix);

		/** render the geometry of this feature */
		if (geometry != null)
			geometry.render();
		
		GLES20.glDisable(GLES20.GL_BLEND);
		GLES20.glDisable(GLES20.GL_CULL_FACE);
	}

	@Override
	public void enableCullface(boolean cullface) {
		this.enableCullFace = cullface;
	}

	@Override
	public void enableBlending(boolean blending, float alpha) {
		this.enableBlending = blending;
		this.alpha = alpha;
	}

	@Override
	public void enableDepthtest(boolean depthTest) {
		this.enableDepthTest = depthTest;
	}

	@Override
	public void setDrawingMode(int drawingMode) {
		this.drawingMode = drawingMode;
	}

	@Override
	public void setColor(int androidColor) {
		this.androidColor = androidColor;
	}

	@Override
	public void setColor(float[] colorArray) {
		throw new UnsupportedOperationException();
	}

	@Override
	public void setTextureCallback(Callable<Bitmap> callback) {
		this.texture = Texture.createInstance(callback);
	}

	@Override
	public void setSubVisualization(DataSourceVisualizationGL subVisualizationGL) {
		if (this.childrenFeatures.contains(subVisualizationGL))
			return;
		this.childrenFeatures.add((OpenGLCallable) subVisualizationGL);
	}

	public void setLightPosition(float[] lightPosInEyeSpace) {
		GLES20.glUniform3f(renderer.getLightPosHandle(), lightPosInEyeSpace[0],
				lightPosInEyeSpace[1], lightPosInEyeSpace[2]);
	}
}
 public class CubeFeature2 extends RenderFeature2 {
	

	// X, Y, Z
	// Unused
	final float[] cubePositionData =
	{
			// In OpenGL counter-clockwise winding is default. This means that when we look at a triangle, 
			// if the points are counter-clockwise we are looking at the "front". If not we are looking at
			// the back. OpenGL has an optimization where all back-facing triangles are culled, since they
			// usually represent the backside of an object and aren't visible anyways.
			
			// Front face
			-1.0f, 1.0f, 1.0f,				
			-1.0f, -1.0f, 1.0f,
			1.0f, 1.0f, 1.0f, 
			-1.0f, -1.0f, 1.0f, 				
			1.0f, -1.0f, 1.0f,
			1.0f, 1.0f, 1.0f,
			
			// Right face
			1.0f, 1.0f, 1.0f,				
			1.0f, -1.0f, 1.0f,
			1.0f, 1.0f, -1.0f,
			1.0f, -1.0f, 1.0f,				
			1.0f, -1.0f, -1.0f,
			1.0f, 1.0f, -1.0f,
			
			// Back face
			1.0f, 1.0f, -1.0f,				
			1.0f, -1.0f, -1.0f,
			-1.0f, 1.0f, -1.0f,
			1.0f, -1.0f, -1.0f,				
			-1.0f, -1.0f, -1.0f,
			-1.0f, 1.0f, -1.0f,
			
			// Left face
			-1.0f, 1.0f, -1.0f,				
			-1.0f, -1.0f, -1.0f,
			-1.0f, 1.0f, 1.0f, 
			-1.0f, -1.0f, -1.0f,				
			-1.0f, -1.0f, 1.0f, 
			-1.0f, 1.0f, 1.0f, 
			
			// Top face
			-1.0f, 1.0f, -1.0f,				
			-1.0f, 1.0f, 1.0f, 
			1.0f, 1.0f, -1.0f, 
			-1.0f, 1.0f, 1.0f, 				
			1.0f, 1.0f, 1.0f, 
			1.0f, 1.0f, -1.0f,
			
			// Bottom face
			1.0f, -1.0f, -1.0f,				
			1.0f, -1.0f, 1.0f, 
			-1.0f, -1.0f, -1.0f,
			1.0f, -1.0f, 1.0f, 				
			-1.0f, -1.0f, 1.0f,
			-1.0f, -1.0f, -1.0f,
	};	
	
	// R, G, B, A
	// TODO unused
	final float[] cubeColorData =
	{				
			// Front face (red)
			1.0f, 0.0f, 0.0f, 1.0f,				
			1.0f, 0.0f, 0.0f, 1.0f,
			1.0f, 0.0f, 0.0f, 1.0f,
			1.0f, 0.0f, 0.0f, 1.0f,				
			1.0f, 0.0f, 0.0f, 1.0f,
			1.0f, 0.0f, 0.0f, 1.0f,
			
			// Right face (green)
			0.0f, 1.0f, 0.0f, 1.0f,				
			0.0f, 1.0f, 0.0f, 1.0f,
			0.0f, 1.0f, 0.0f, 1.0f,
			0.0f, 1.0f, 0.0f, 1.0f,				
			0.0f, 1.0f, 0.0f, 1.0f,
			0.0f, 1.0f, 0.0f, 1.0f,
			
			// Back face (blue)
			0.0f, 0.0f, 1.0f, 1.0f,				
			0.0f, 0.0f, 1.0f, 1.0f,
			0.0f, 0.0f, 1.0f, 1.0f,
			0.0f, 0.0f, 1.0f, 1.0f,				
			0.0f, 0.0f, 1.0f, 1.0f,
			0.0f, 0.0f, 1.0f, 1.0f,
			
			// Left face (yellow)
			1.0f, 1.0f, 0.0f, 1.0f,				
			1.0f, 1.0f, 0.0f, 1.0f,
			1.0f, 1.0f, 0.0f, 1.0f,
			1.0f, 1.0f, 0.0f, 1.0f,				
			1.0f, 1.0f, 0.0f, 1.0f,
			1.0f, 1.0f, 0.0f, 1.0f,
			
			// Top face (cyan)
			0.0f, 1.0f, 1.0f, 1.0f,				
			0.0f, 1.0f, 1.0f, 1.0f,
			0.0f, 1.0f, 1.0f, 1.0f,
			0.0f, 1.0f, 1.0f, 1.0f,				
			0.0f, 1.0f, 1.0f, 1.0f,
			0.0f, 1.0f, 1.0f, 1.0f,
			
			// Bottom face (magenta)
			1.0f, 0.0f, 1.0f, 1.0f,				
			1.0f, 0.0f, 1.0f, 1.0f,
			1.0f, 0.0f, 1.0f, 1.0f,
			1.0f, 0.0f, 1.0f, 1.0f,				
			1.0f, 0.0f, 1.0f, 1.0f,
			1.0f, 0.0f, 1.0f, 1.0f
	};
	
	// X, Y, Z
	// The normal is used in light calculations and is a vector which points
	// orthogonal to the plane of the surface. For a cube model, the normals
	// should be orthogonal to the points of each face.
	// TODO Unused
	final float[] cubeNormalData =
	{												
			// Front face
			0.0f, 0.0f, 1.0f,				
			0.0f, 0.0f, 1.0f,
			0.0f, 0.0f, 1.0f,
			0.0f, 0.0f, 1.0f,				
			0.0f, 0.0f, 1.0f,
			0.0f, 0.0f, 1.0f,
			
			// Right face 
			1.0f, 0.0f, 0.0f,				
			1.0f, 0.0f, 0.0f,
			1.0f, 0.0f, 0.0f,
			1.0f, 0.0f, 0.0f,				
			1.0f, 0.0f, 0.0f,
			1.0f, 0.0f, 0.0f,
			
			// Back face 
			0.0f, 0.0f, -1.0f,				
			0.0f, 0.0f, -1.0f,
			0.0f, 0.0f, -1.0f,
			0.0f, 0.0f, -1.0f,				
			0.0f, 0.0f, -1.0f,
			0.0f, 0.0f, -1.0f,
			
			// Left face 
			-1.0f, 0.0f, 0.0f,				
			-1.0f, 0.0f, 0.0f,
			-1.0f, 0.0f, 0.0f,
			-1.0f, 0.0f, 0.0f,				
			-1.0f, 0.0f, 0.0f,
			-1.0f, 0.0f, 0.0f,
			
			// Top face 
			0.0f, 1.0f, 0.0f,			
			0.0f, 1.0f, 0.0f,
			0.0f, 1.0f, 0.0f,
			0.0f, 1.0f, 0.0f,				
			0.0f, 1.0f, 0.0f,
			0.0f, 1.0f, 0.0f,
			
			// Bottom face 
			0.0f, -1.0f, 0.0f,			
			0.0f, -1.0f, 0.0f,
			0.0f, -1.0f, 0.0f,
			0.0f, -1.0f, 0.0f,				
			0.0f, -1.0f, 0.0f,
			0.0f, -1.0f, 0.0f
	};
	
	// S, T (or X, Y)
	// Texture coordinate data.
	// Because images have a Y axis pointing downward (values increase as you move down the image) while
	// OpenGL has a Y axis pointing upward, we adjust for that here by flipping the Y axis.
	// What's more is that the texture coordinates are the same for every face.
	final float[] cubeTextureCoordinateData =
	{												
			// Front face
			0.0f, 0.0f, 				
			0.0f, 1.0f,
			1.0f, 0.0f,
			0.0f, 1.0f,
			1.0f, 1.0f,
			1.0f, 0.0f,				
			
			// Right face 
			0.0f, 0.0f, 				
			0.0f, 1.0f,
			1.0f, 0.0f,
			0.0f, 1.0f,
			1.0f, 1.0f,
			1.0f, 0.0f,	
			
			// Back face 
			0.0f, 0.0f, 				
			0.0f, 1.0f,
			1.0f, 0.0f,
			0.0f, 1.0f,
			1.0f, 1.0f,
			1.0f, 0.0f,	
			
			// Left face 
			0.0f, 0.0f, 				
			0.0f, 1.0f,
			1.0f, 0.0f,
			0.0f, 1.0f,
			1.0f, 1.0f,
			1.0f, 0.0f,	
			
			// Top face 
			0.0f, 0.0f, 				
			0.0f, 1.0f,
			1.0f, 0.0f,
			0.0f, 1.0f,
			1.0f, 1.0f,
			1.0f, 0.0f,	
			
			// Bottom face 
			0.0f, 0.0f, 				
			0.0f, 1.0f,
			1.0f, 0.0f,
			0.0f, 1.0f,
			1.0f, 1.0f,
			1.0f, 0.0f
	};

	/*****************************
	 * static variables
	 *****************************/
	
	// R, G, B, A
	private final float[] colors =
	{				
			// Front face (red)
			1.0f, 0.0f, 0.0f, 1.0f,				
			1.0f, 0.0f, 0.0f, 1.0f,
			1.0f, 0.0f, 0.0f, 1.0f,
			1.0f, 0.0f, 0.0f, 1.0f,				
			1.0f, 0.0f, 0.0f, 1.0f,
			1.0f, 0.0f, 0.0f, 1.0f,
			
			// Right face (green)
			0.0f, 1.0f, 0.0f, 1.0f,				
			0.0f, 1.0f, 0.0f, 1.0f,
			0.0f, 1.0f, 0.0f, 1.0f,
			0.0f, 1.0f, 0.0f, 1.0f,				
			0.0f, 1.0f, 0.0f, 1.0f,
			0.0f, 1.0f, 0.0f, 1.0f,
			
			// Back face (blue)
			0.0f, 0.0f, 1.0f, 1.0f,				
			0.0f, 0.0f, 1.0f, 1.0f,
			0.0f, 0.0f, 1.0f, 1.0f,
			0.0f, 0.0f, 1.0f, 1.0f,				
			0.0f, 0.0f, 1.0f, 1.0f,
			0.0f, 0.0f, 1.0f, 1.0f,
			
			// Left face (yellow)
			1.0f, 1.0f, 0.0f, 1.0f,				
			1.0f, 1.0f, 0.0f, 1.0f,
			1.0f, 1.0f, 0.0f, 1.0f,
			1.0f, 1.0f, 0.0f, 1.0f,				
			1.0f, 1.0f, 0.0f, 1.0f,
			1.0f, 1.0f, 0.0f, 1.0f,
			
			// Top face (cyan)
			0.0f, 1.0f, 1.0f, 1.0f,				
			0.0f, 1.0f, 1.0f, 1.0f,
			0.0f, 1.0f, 1.0f, 1.0f,
			0.0f, 1.0f, 1.0f, 1.0f,				
			0.0f, 1.0f, 1.0f, 1.0f,
			0.0f, 1.0f, 1.0f, 1.0f,
			
			// Bottom face (magenta)
			1.0f, 0.0f, 1.0f, 1.0f,				
			1.0f, 0.0f, 1.0f, 1.0f,
			1.0f, 0.0f, 1.0f, 1.0f,
			1.0f, 0.0f, 1.0f, 1.0f,				
			1.0f, 0.0f, 1.0f, 1.0f,
			1.0f, 0.0f, 1.0f, 1.0f
	};

	// R, G, B, A
	private final float[] whitecolors =
	{				
			// Front face (red)
			1.0f, 1.0f, 1.0f, 1.0f,				
			1.0f, 1.0f, 1.0f, 1.0f,
			1.0f, 1.0f, 1.0f, 1.0f,
			1.0f, 1.0f, 1.0f, 1.0f,				
			1.0f, 1.0f, 1.0f, 1.0f,
			1.0f, 1.0f, 1.0f, 1.0f,
			
			// Right face (green)
			1.0f, 1.0f, 1.0f, 1.0f,				
			1.0f, 1.0f, 1.0f, 1.0f,
			1.0f, 1.0f, 1.0f, 1.0f,
			1.0f, 1.0f, 1.0f, 1.0f,				
			1.0f, 1.0f, 1.0f, 1.0f,
			1.0f, 1.0f, 1.0f, 1.0f,
			
			// Back face (blue)
			1.0f, 1.0f, 1.0f, 1.0f,				
			1.0f, 1.0f, 1.0f, 1.0f,
			1.0f, 1.0f, 1.0f, 1.0f,
			1.0f, 1.0f, 1.0f, 1.0f,				
			1.0f, 1.0f, 1.0f, 1.0f,
			1.0f, 1.0f, 1.0f, 1.0f,
			
			// Left face (yellow)
			1.0f, 1.0f, 1.0f, 1.0f,				
			1.0f, 1.0f, 1.0f, 1.0f,
			1.0f, 1.0f, 1.0f, 1.0f,
			1.0f, 1.0f, 1.0f, 1.0f,				
			1.0f, 1.0f, 1.0f, 1.0f,
			1.0f, 1.0f, 1.0f, 1.0f,
			
			// Top face (cyan)
			1.0f, 1.0f, 1.0f, 1.0f,				
			1.0f, 1.0f, 1.0f, 1.0f,
			1.0f, 1.0f, 1.0f, 1.0f,
			1.0f, 1.0f, 1.0f, 1.0f,				
			1.0f, 1.0f, 1.0f, 1.0f,
			1.0f, 1.0f, 1.0f, 1.0f,
			
			// Bottom face (magenta)
			1.0f, 1.0f, 1.0f, 1.0f,				
			1.0f, 1.0f, 1.0f, 1.0f,
			1.0f, 1.0f, 1.0f, 1.0f,
			1.0f, 1.0f, 1.0f, 1.0f,				
			1.0f, 1.0f, 1.0f, 1.0f,
			1.0f, 1.0f, 1.0f, 1.0f
	};

	
	
	private final float[] vertices =
	{
			// In OpenGL counter-clockwise winding is default. This means that when we look at a triangle, 
			// if the points are counter-clockwise we are looking at the "front". If not we are looking at
			// the back. OpenGL has an optimization where all back-facing triangles are culled, since they
			// usually represent the backside of an object and aren't visible anyways.
			
			// Front face
			-0.5f, 0.5f, 0.5f,				
			-0.5f, -0.5f, 0.5f,
			0.5f, 0.5f, 0.5f, 
			-0.5f, -0.5f, 0.5f, 				
			0.5f, -0.5f, 0.5f,
			0.5f, 0.5f, 0.5f,
			
			// Right face
			0.5f, 0.5f, 0.5f,				
			0.5f, -0.5f, 0.5f,
			0.5f, 0.5f, -0.5f,
			0.5f, -0.5f, 0.5f,				
			0.5f, -0.5f, -0.5f,
			0.5f, 0.5f, -0.5f,
			
			// Back face
			0.5f, 0.5f, -0.5f,				
			0.5f, -0.5f, -0.5f,
			-0.5f, 0.5f, -0.5f,
			0.5f, -0.5f, -0.5f,				
			-0.5f, -0.5f, -0.5f,
			-0.5f, 0.5f, -0.5f,
			
			// Left face
			-0.5f, 0.5f, -0.5f,				
			-0.5f, -0.5f, -0.5f,
			-0.5f, 0.5f, 0.5f, 
			-0.5f, -0.5f, -0.5f,				
			-0.5f, -0.5f, 0.5f, 
			-0.5f, 0.5f, 0.5f, 
			
			// Top face
			-0.5f, 0.5f, -0.5f,				
			-0.5f, 0.5f, 0.5f, 
			0.5f, 0.5f, -0.5f, 
			-0.5f, 0.5f, 0.5f, 				
			0.5f, 0.5f, 0.5f, 
			0.5f, 0.5f, -0.5f,
			
			// Bottom face
			0.5f, -0.5f, -0.5f,				
			0.5f, -0.5f, 0.5f, 
			-0.5f, -0.5f, -0.5f,
			0.5f, -0.5f, 0.5f, 				
			-0.5f, -0.5f, 0.5f,
			-0.5f, -0.5f, -0.5f,
	};	
	
	/** standard vertices array */
	private final float[] normals = {
			// Front face
			0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f,
			0.0f, 0.0f,
			1.0f,
			0.0f,
			0.0f,
			1.0f,
			0.0f,
			0.0f,
			1.0f,

			// Right face
			1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f,
			1.0f, 0.0f, 0.0f,
			1.0f,
			0.0f,
			0.0f,
			1.0f,
			0.0f,
			0.0f,

			// Back face
			0.0f, 0.0f, -1.0f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f,
			-1.0f, 0.0f, 0.0f, -1.0f, 0.0f,
			0.0f,
			-1.0f,
			0.0f,
			0.0f,
			-1.0f,

			// Left face
			-1.0f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f, -1.0f, 0.0f,
			0.0f, -1.0f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f,
			-1.0f,
			0.0f,
			0.0f,

			// Top face
			0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f,
			0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f,
			0.0f,

			// Bottom face
			0.0f, -1.0f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f, -1.0f,
			0.0f, 0.0f, -1.0f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f,
			-1.0f, 0.0f };


	
//	/** standard indices array */
//	private static final short[] indices = { 0, 4, 5, 0, 5, 1, 1, 5, 6, 1, 6,
//			2, 2, 6, 7, 2, 7, 3, 3, 7, 4, 3, 4, 0, 4, 7, 6, 4, 6, 5, 3, 0, 1,
//			3, 1, 2 };

	/***************************
	 * Constructor
	 ***************************/
	public CubeFeature2() {
		this.drawingMode = GLES20.GL_TRIANGLES;
		this.heightOffset = -0.5f;
	}

	@Override
	public void onCreateInGLESThread() {
		setRenderObjectives(vertices, whitecolors, normals, cubeTextureCoordinateData);
	}

	@Override
	public void setOpenGLPreRenderingSettings() {

	}

	@Override
	public void onPreRender() {

	}


	
	
//	/** standard color array */
//	private static final float[] whitecolors = { 
//		1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
//		1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 
//		1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 
//		1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 
//		1, 1, 1, 1, 1, 1, 1, 1,	1, 1, 1, 1, 
//		1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 
//		1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 
//		1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 };
//
//	/** standard vertices array */
//	private static final float[] vertices = {
//        -0.5f, 	-0.5f, 	-0.5f,
//        0.5f, 	-0.5f, 	-0.5f,
//        0.5f,  	0.5f, 	-0.5f,
//        -0.5f,  0.5f, 	-0.5f,
//        -0.5f, 	-0.5f,  0.5f,
//        0.5f, 	-0.5f,  0.5f,
//        0.5f,  	0.5f,  	0.5f,
//        -0.5f,  0.5f,  	0.5f,
//	};
//
//	/** standard normals array */
//	private static final float[] normals = { 
//			0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, // front
//			1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, // right
//			0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, // back
//			-1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, // left
//			0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, // top
//			0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, // bottom
//	};
//
//	/** standard indices array */
//	private static final short[] indices = {
//        0, 4, 5,    0, 5, 1,
//        1, 5, 6,    1, 6, 2,
//        2, 6, 7,    2, 7, 3,
//        3, 7, 4,    3, 4, 0,
//        4, 7, 6,    4, 6, 5,
//        3, 0, 1,    3, 1, 2
//	};
}
 public class SphereFeature extends RenderFeature2 {

	@Override
	public void setOpenGLPreRenderingSettings() {
		// TODO Auto-generated method stub
		
	}

	@Override
	public void onPreRender() {
		// TODO Auto-generated method stub
		
	}

	@Override
	public void onCreateInGLESThread() {
		// TODO Auto-generated method stub
		
	}
}

import java.util.ArrayList;
import java.util.List;
import org.n52.geoar.GeoARApplication;
import org.n52.geoar.R;
import org.n52.geoar.alg.proj.MercatorPoint;
import org.n52.geoar.alg.proj.MercatorProj;
import org.n52.geoar.alg.proj.MercatorRect;
import org.n52.geoar.newdata.DataCache.Cancelable;
import org.n52.geoar.newdata.DataCache.DataSourceErrorType;
import org.n52.geoar.newdata.DataCache.GetDataBoundsCallback;
import org.n52.geoar.newdata.DataSourceInstanceHolder;
import org.n52.geoar.newdata.DataSourceInstanceHolder.DataSourceSettingsChangedListener;
import org.n52.geoar.newdata.RenderFeatureFactory;
import org.n52.geoar.newdata.SpatialEntity2;
import org.n52.geoar.newdata.Visualization.ARVisualization;
import org.n52.geoar.newdata.Visualization.ARVisualization.ItemVisualization;
import org.n52.geoar.newdata.vis.DataSourceVisualization.DataSourceVisualizationGL;
import org.n52.geoar.utils.GeoLocation;
import org.n52.geoar.view.InfoView;
import org.n52.geoar.view.geoar.Settings;
import org.n52.geoar.view.geoar.gl.mode.RenderFeature2;
import org.n52.geoar.view.geoar.gl.mode.features.CubeFeature2;
import org.n52.geoar.view.geoar.gl.mode.features.SphereFeature;
import com.vividsolutions.jts.geom.Geometry;

/**
 * Copyright 2012 52°North Initiative for Geospatial Open Source Software GmbH
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.n52.geoar.ar.view;




/**
 * 
 * @author Arne de Wall <a.dewall@52North.org>
 *
 */
public class DataSourceVisualizationHandler implements RenderFeatureFactory,
		DataSourceSettingsChangedListener {

	private GetDataBoundsCallback callback = new GetDataBoundsCallback() {

		@Override
		public void onProgressUpdate(int progress, int maxProgress) {
			InfoView.setProgressTitle(R.string.requesting_data,
					DataSourceVisualizationHandler.this);
			InfoView.setProgress(progress, maxProgress,
					DataSourceVisualizationHandler.this);

		}

		@Override
		public void onAbort(MercatorRect bounds, DataSourceErrorType reason) {
			InfoView.clearProgress(DataSourceVisualizationHandler.this);
			if (reason == DataSourceErrorType.CONNECTION) {
				InfoView.setStatus(R.string.connection_error, 5000,
						DataSourceVisualizationHandler.this);
			} else if (reason == DataSourceErrorType.UNKNOWN) {
				InfoView.setStatus(R.string.unknown_error, 5000,
						DataSourceVisualizationHandler.this);
			}
		}

		@Override
		public void onReceiveDataUpdate(MercatorRect bounds,
				List<? extends SpatialEntity2<? extends Geometry>> data) {

			synchronized (mutex) {
				List<ARObject> arObjects = new ArrayList<ARObject>();
				List<ItemVisualization> visualizations = dataSourceInstance
						.getParent()
						.getVisualizations()
						.getCheckedItems(
								ARVisualization.ItemVisualization.class);

				for (SpatialEntity2<? extends Geometry> entity : data) {

					for (ItemVisualization visualization : visualizations) {
						// XXX FIXME TODO List is no longer needed
						List<RenderFeature2> features = new ArrayList<RenderFeature2>();
						RenderFeature2 feature = (RenderFeature2) visualization
								.getEntityVisualization(entity,
										DataSourceVisualizationHandler.this);
						features.add(feature);
						// for (DataSourceVisualizationGL feature :
						// visualization
						// .getEntityVisualization(entity,
						// DataSourceVisualizationHandler.this)) {
						// features.add((RenderFeature2) feature);
						// }
						ARObject arObject = new ARObject(entity, visualization,
								features,
								visualization.getEntityVisualization(entity),
								visualization.getFeatureDetailView(entity,
										null, null,
										GeoARApplication.applicationContext),
								dataSourceInstance);
						// TODO maybe just use entity + visualization
						arObjects.add(arObject);
					}

				}

				arView.setARObjects(arObjects, dataSourceInstance);
				currentRect = bounds;
			}
		}
	};

	private DataSourceInstanceHolder dataSourceInstance;
	private Object mutex = new Object();

	// public List<RenderFeature> renderFeatures = new
	// ArrayList<RenderFeature>();
	private GeoLocation currentCenterGPoint;
	private MercatorPoint currentCenterMercator;
	private MercatorRect currentRect;

	private Cancelable currentUpdate;

	private ARView arView;

	public DataSourceVisualizationHandler(ARView arView,
			DataSourceInstanceHolder dataSourceInstance) {
		this.arView = arView;
		this.dataSourceInstance = dataSourceInstance;

		// GeoLocation loc = new GeoLocation(LocationHandler
		// .getLastKnownLocation().getLatitude(), LocationHandler
		// .getLastKnownLocation().getLongitude());
		// setCenter(loc);
	}

	public void setCenter(GeoLocation gPoint) {
		currentCenterGPoint = gPoint;

		// Calculate thresholds for request of data
		double meterPerPixel = MercatorProj.getGroundResolution(

				gPoint.getLatitudeE6() / 1E6f, Settings.ZOOM_AR);
