/* * Copyright (C) 2010- Peer internet solutions * * This file is part of mixare. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU General Public License * for more details. * * You should have received a copy of the GNU General Public License along with * this program. If not, see <http://www.gnu.org/licenses/> */ package org.mixare; import java.net.URLDecoder; import java.text.DecimalFormat; import org.mixare.lib.MixContextInterface; import org.mixare.lib.MixStateInterface; import org.mixare.lib.MixUtils; import org.mixare.lib.gui.Label; import org.mixare.lib.gui.PaintScreen; import org.mixare.lib.gui.ScreenLine; import org.mixare.lib.gui.TextObj; import org.mixare.lib.marker.Marker; import org.mixare.lib.marker.draw.ParcelableProperty; import org.mixare.lib.marker.draw.PrimitiveProperty; import org.mixare.lib.reality.PhysicalPlace; import org.mixare.lib.render.Camera; import org.mixare.lib.render.MixVector; import android.graphics.Bitmap; import android.location.Location; /** * The class represents a marker and contains its information. * It draws the marker itself and the corresponding label. * All markers are specific markers like SocialMarkers or * NavigationMarkers, since this class is abstract */ public abstract class LocalMarker implements Marker { private String ID; protected String title; protected boolean underline = false; private String URL; protected PhysicalPlace mGeoLoc; // distance from user to mGeoLoc in meters protected double distance; // The marker color private int colour; private boolean active; // Draw properties protected boolean isVisible; // private boolean isLookingAt; // private boolean isNear; // private float deltaCenter; public MixVector cMarker = new MixVector(); protected MixVector signMarker = new MixVector(); // private MixVector oMarker = new MixVector(); protected MixVector locationVector = new MixVector(); private MixVector origin = new MixVector(0, 0, 0); private MixVector upV = new MixVector(0, 1, 0); private ScreenLine pPt = new ScreenLine(); public Label txtLab = new Label(); protected TextObj textBlock; public LocalMarker(String id, String title, double latitude, double longitude, double altitude, String link, int type, int colour) { super(); this.active = false; this.title = title; this.mGeoLoc = new PhysicalPlace(latitude,longitude,altitude); if (link != null && link.length() > 0) { URL = "webpage:" + URLDecoder.decode(link); this.underline = true; } this.colour = colour; this.ID = id + "##" + type + "##" + title; } public String getTitle(){ return title; } public String getURL(){ return URL; } public double getLatitude() { return mGeoLoc.getLatitude(); } public double getLongitude() { return mGeoLoc.getLongitude(); } public double getAltitude() { return mGeoLoc.getAltitude(); } public MixVector getLocationVector() { return locationVector; } private void cCMarker(MixVector originalPoint, Camera viewCam, float addX, float addY) { // Temp properties MixVector tmpa = new MixVector(originalPoint); MixVector tmpc = new MixVector(upV); tmpa.add(locationVector); //3 tmpc.add(locationVector); //3 tmpa.sub(viewCam.lco); //4 tmpc.sub(viewCam.lco); //4 tmpa.prod(viewCam.transform); //5 tmpc.prod(viewCam.transform); //5 MixVector tmpb = new MixVector(); viewCam.projectPoint(tmpa, tmpb, addX, addY); //6 cMarker.set(tmpb); //7 viewCam.projectPoint(tmpc, tmpb, addX, addY); //6 signMarker.set(tmpb); //7 } private void calcV(Camera viewCam) { isVisible = false; // isLookingAt = false; // deltaCenter = Float.MAX_VALUE; if (cMarker.z < -1f) { isVisible = true; } } public void update(Location curGPSFix) { // An elevation of 0.0 probably means that the elevation of the // POI is not known and should be set to the users GPS height // Note: this could be improved with calls to // http://www.geonames.org/export/web-services.html#astergdem // to estimate the correct height with DEM models like SRTM, AGDEM or GTOPO30 if(mGeoLoc.getAltitude()==0.0) mGeoLoc.setAltitude(curGPSFix.getAltitude()); // compute the relative position vector from user position to POI location PhysicalPlace.convLocToVec(curGPSFix, mGeoLoc, locationVector); } public void calcPaint(Camera viewCam, float addX, float addY) { cCMarker(origin, viewCam, addX, addY); calcV(viewCam); } // private void calcPaint(Camera viewCam) { // cCMarker(origin, viewCam, 0, 0); // } private boolean isClickValid(float x, float y) { //if the marker is not active (i.e. not shown in AR view) we don't have to check it for clicks if (!isActive() && !this.isVisible) return false; float currentAngle = MixUtils.getAngle(cMarker.x, cMarker.y, signMarker.x, signMarker.y); //TODO adapt the following to the variable radius! pPt.x = x - signMarker.x; pPt.y = y - signMarker.y; pPt.rotate((float) Math.toRadians(-(currentAngle + 90))); pPt.x += txtLab.getX(); pPt.y += txtLab.getY(); float objX = txtLab.getX() - txtLab.getWidth() / 2; float objY = txtLab.getY() - txtLab.getHeight() / 2; float objW = txtLab.getWidth(); float objH = txtLab.getHeight(); if (pPt.x > objX && pPt.x < objX + objW && pPt.y > objY && pPt.y < objY + objH) { return true; } else { return false; } } public void draw(PaintScreen dw) { drawCircle(dw); drawTextBlock(dw); } public void drawCircle(PaintScreen dw) { if (isVisible) { //float maxHeight = Math.round(dw.getHeight() / 10f) + 1; float maxHeight = dw.getHeight(); dw.setStrokeWidth(maxHeight / 100f); dw.setFill(false); //dw.setColor(DataSource.getColor(type)); //draw circle with radius depending on distance //0.44 is approx. vertical fov in radians double angle = 2.0*Math.atan2(10,distance); double radius = Math.max(Math.min(angle/0.44 * maxHeight, maxHeight),maxHeight/25f); //double radius = angle/0.44d * (double)maxHeight; dw.paintCircle(cMarker.x, cMarker.y, (float)radius); } } public void drawTextBlock(PaintScreen dw) { //TODO: grandezza cerchi e trasparenza float maxHeight = Math.round(dw.getHeight() / 10f) + 1; //TODO: change textblock only when distance changes String textStr=""; double d = distance; DecimalFormat df = new DecimalFormat("@#"); if(d<1000.0) { textStr = title + " ("+ df.format(d) + "m)"; } else { d=d/1000.0; textStr = title + " (" + df.format(d) + "km)"; } textBlock = new TextObj(textStr, Math.round(maxHeight / 2f) + 1, 250, dw, underline); if (isVisible) { //dw.setColor(DataSource.getColor(type)); float currentAngle = MixUtils.getAngle(cMarker.x, cMarker.y, signMarker.x, signMarker.y); txtLab.prepare(textBlock); dw.setStrokeWidth(1f); dw.setFill(true); dw.paintObj(txtLab, signMarker.x - txtLab.getWidth() / 2, signMarker.y + maxHeight, currentAngle + 90, 1); } } public boolean fClick(float x, float y, MixContextInterface ctx, MixStateInterface state) { boolean evtHandled = false; if (isClickValid(x, y)) { evtHandled = state.handleEvent(ctx, URL); } return evtHandled; } public double getDistance() { return distance; } public void setDistance(double distance) { this.distance = distance; } public String getID() { return ID; } public void setID(String iD) { ID = iD; } public int compareTo(Marker another) { Marker leftPm = this; Marker rightPm = another; return Double.compare(leftPm.getDistance(), rightPm.getDistance()); } @Override public boolean equals (Object marker) { return this.ID.equals(((Marker) marker).getID()); } @Override public int hashCode() { return this.ID.hashCode(); } public boolean isActive() { return active; } public void setActive(boolean active) { this.active = active; } abstract public int getMaxObjects(); public void setImage(Bitmap image){ } public Bitmap getImage(){ return null; } //get Colour for OpenStreetMap based on the URL number public int getColour() { return colour; } @Override public void setTxtLab(Label txtLab) { this.txtLab = txtLab; } @Override public Label getTxtLab() { return txtLab; } public void setExtras(String name, PrimitiveProperty primitiveProperty){ //nothing to add } public void setExtras(String name, ParcelableProperty parcelableProperty){ //nothing to add } }