add osmdroid overlays + bonuspack
BIN
res/drawable-mdpi/bonuspack_bubble.9.png
Normal file
|
After Width: | Height: | Size: 1.6 KiB |
BIN
res/drawable-mdpi/bonuspack_bubble_black.9.png
Normal file
|
After Width: | Height: | Size: 593 B |
BIN
res/drawable-mdpi/delete.png
Normal file
|
After Width: | Height: | Size: 2.6 KiB |
BIN
res/drawable-mdpi/delete_pressed.png
Normal file
|
After Width: | Height: | Size: 2.5 KiB |
BIN
res/drawable-mdpi/ic_continue.png
Normal file
|
After Width: | Height: | Size: 414 B |
BIN
res/drawable-mdpi/ic_empty.png
Normal file
|
After Width: | Height: | Size: 240 B |
BIN
res/drawable-mdpi/moreinfo_arrow.png
Normal file
|
After Width: | Height: | Size: 1.5 KiB |
BIN
res/drawable-mdpi/moreinfo_arrow_pressed.png
Normal file
|
After Width: | Height: | Size: 1.5 KiB |
BIN
res/drawable-nodpi/marker_departure.png
Normal file
|
After Width: | Height: | Size: 1.4 KiB |
BIN
res/drawable-nodpi/marker_destination.png
Normal file
|
After Width: | Height: | Size: 1.1 KiB |
BIN
res/drawable-nodpi/marker_node.png
Normal file
|
After Width: | Height: | Size: 611 B |
BIN
res/drawable-nodpi/marker_poi_default.png
Normal file
|
After Width: | Height: | Size: 610 B |
BIN
res/drawable-nodpi/marker_poi_flickr.png
Normal file
|
After Width: | Height: | Size: 490 B |
BIN
res/drawable-nodpi/marker_poi_picasa_24.png
Normal file
|
After Width: | Height: | Size: 2.2 KiB |
BIN
res/drawable-nodpi/marker_poi_wikipedia_16.png
Normal file
|
After Width: | Height: | Size: 1.4 KiB |
BIN
res/drawable-nodpi/marker_poi_wikipedia_32.png
Normal file
|
After Width: | Height: | Size: 2.1 KiB |
BIN
res/drawable-nodpi/marker_via.png
Normal file
|
After Width: | Height: | Size: 1.1 KiB |
8
res/drawable/btn_delete.xml
Normal file
@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:drawable="@drawable/delete_pressed"
|
||||
android:state_pressed="true" />
|
||||
<item android:drawable="@drawable/delete"
|
||||
android:state_focused="true" />
|
||||
<item android:drawable="@drawable/delete" />
|
||||
</selector>
|
||||
8
res/drawable/btn_moreinfo.xml
Normal file
@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:drawable="@drawable/moreinfo_arrow_pressed"
|
||||
android:state_pressed="true" />
|
||||
<item android:drawable="@drawable/moreinfo_arrow"
|
||||
android:state_focused="true" />
|
||||
<item android:drawable="@drawable/moreinfo_arrow" />
|
||||
</selector>
|
||||
@ -8,7 +8,13 @@
|
||||
<org.oscim.view.MapView
|
||||
android:id="@+id/mapView"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="fill_parent"/>
|
||||
android:layout_height="fill_parent" />
|
||||
|
||||
<RelativeLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/root"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="fill_parent" />
|
||||
|
||||
<ToggleButton
|
||||
android:id="@+id/snapToLocationView"
|
||||
|
||||
53
res/layout/bonuspack_bubble.xml
Normal file
@ -0,0 +1,53 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:background="@drawable/bonuspack_bubble_black" >
|
||||
<ImageView android:id="@+id/bubble_image"
|
||||
android:layout_width="65dp"
|
||||
android:layout_height="65dp"
|
||||
android:paddingLeft="5dp"
|
||||
android:visibility="gone" />
|
||||
<LinearLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical" >
|
||||
<LinearLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal" >
|
||||
<TextView android:id="@+id/bubble_title"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textColor="#FFFFFF"
|
||||
android:maxEms="17"
|
||||
android:layout_gravity="left"
|
||||
android:layout_weight="1"
|
||||
android:text="Title" />
|
||||
<Button android:id="@+id/bubble_moreinfo"
|
||||
android:background="@drawable/btn_moreinfo"
|
||||
android:visibility="gone"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="right"
|
||||
android:layout_weight="0" />
|
||||
</LinearLayout>
|
||||
<TextView android:id="@+id/bubble_description"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textColor="#FFFFFF"
|
||||
android:textSize="12dp"
|
||||
android:maxEms="17"
|
||||
android:text="Description" />
|
||||
<TextView android:id="@+id/bubble_subdescription"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textColor="#FFFFFF"
|
||||
android:textSize="10dp"
|
||||
android:maxEms="17"
|
||||
android:text="Address"
|
||||
android:visibility="gone"
|
||||
/>
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
34
res/layout/item_layout.xml
Normal file
@ -0,0 +1,34 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal" >
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/thumbnail"
|
||||
android:layout_width="65dp"
|
||||
android:layout_height="65dp"
|
||||
android:scaleType="centerCrop"
|
||||
android:src="@drawable/ic_continue" />
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical" >
|
||||
|
||||
<TextView
|
||||
android:id="@+id/title"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="instructions"
|
||||
android:textColor="#000000" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/details"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="distance/duration"
|
||||
android:textColor="#555555" />
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
46
res/layout/items_list.xml
Normal file
@ -0,0 +1,46 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<LinearLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="fill_parent"
|
||||
android:background="#55000000"
|
||||
android:orientation="vertical">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center|clip_vertical"
|
||||
android:layout_marginBottom="10dp"
|
||||
android:layout_marginLeft="10dp"
|
||||
android:layout_marginRight="10dp"
|
||||
android:layout_marginTop="10dp"
|
||||
android:orientation="vertical"
|
||||
android:background="#FFFFFF"
|
||||
android:padding="10dp">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<AutoCompleteTextView
|
||||
android:id="@+id/poiTag"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:ems="10"
|
||||
android:completionThreshold="1" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/buttonSetPOITag"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Search" />
|
||||
</LinearLayout>
|
||||
|
||||
<ListView
|
||||
android:id="@+id/items"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="fill_parent" />
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
49
res/layout/itinerary_bubble.xml
Normal file
@ -0,0 +1,49 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:background="@drawable/bonuspack_bubble_black" >
|
||||
<ImageView android:id="@+id/bubble_image"
|
||||
android:layout_width="65dp"
|
||||
android:layout_height="65dp"
|
||||
android:paddingLeft="5dp"
|
||||
android:visibility="gone" />
|
||||
<LinearLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical" >
|
||||
<LinearLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal" >
|
||||
<TextView android:id="@+id/bubble_title"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textColor="#FFFFFF"
|
||||
android:maxEms="17"
|
||||
android:text="Title" />
|
||||
</LinearLayout>
|
||||
<TextView android:id="@+id/bubble_description"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textColor="#FFFFFF"
|
||||
android:textSize="12dp"
|
||||
android:maxEms="17"
|
||||
android:text="Description" />
|
||||
<TextView android:id="@+id/bubble_subdescription"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textColor="#FFFFFF"
|
||||
android:textSize="10dp"
|
||||
android:maxEms="17"
|
||||
android:text="Address"
|
||||
android:visibility="gone" />
|
||||
</LinearLayout>
|
||||
<Button android:id="@+id/bubble_delete"
|
||||
android:background="@drawable/btn_delete"
|
||||
android:visibility="visible"
|
||||
android:layout_width="25dp"
|
||||
android:layout_height="25dp"
|
||||
android:layout_gravity="right" />
|
||||
</LinearLayout>
|
||||
52
res/layout/osmdroid/bonuspack_bubble.xml
Normal file
@ -0,0 +1,52 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:background="@drawable/bonuspack_bubble" >
|
||||
<ImageView android:id="@+id/bubble_image"
|
||||
android:layout_width="65dp"
|
||||
android:layout_height="65dp"
|
||||
android:visibility="gone" />
|
||||
<LinearLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingLeft="5dp"
|
||||
android:orientation="vertical" >
|
||||
<LinearLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal" >
|
||||
<TextView android:id="@+id/bubble_title"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textColor="#000000"
|
||||
android:maxEms="17"
|
||||
android:layout_gravity="left"
|
||||
android:layout_weight="1"
|
||||
android:text="Title" />
|
||||
<Button android:id="@+id/bubble_moreinfo"
|
||||
android:background="@drawable/btn_moreinfo"
|
||||
android:visibility="gone"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="right"
|
||||
android:layout_weight="0" />
|
||||
</LinearLayout>
|
||||
<TextView android:id="@+id/bubble_description"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textColor="#000000"
|
||||
android:textSize="12dp"
|
||||
android:maxEms="17"
|
||||
android:text="Description" />
|
||||
<TextView android:id="@+id/bubble_subdescription"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textColor="#000000"
|
||||
android:textSize="10dp"
|
||||
android:maxEms="17"
|
||||
android:text="Address"
|
||||
android:visibility="gone" />
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
69
res/layout/osmdroid/main.xml
Normal file
@ -0,0 +1,69 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="fill_parent"
|
||||
android:orientation="vertical" >
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal" >
|
||||
|
||||
<EditText
|
||||
android:id="@+id/editDestination"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="left"
|
||||
android:layout_weight="1"
|
||||
android:hint="@string/destination" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/buttonSearch"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="right"
|
||||
android:layout_weight="0"
|
||||
android:text="Search" />
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal" >
|
||||
|
||||
<AutoCompleteTextView
|
||||
android:id="@+id/poiTag"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="left"
|
||||
android:layout_weight="1"
|
||||
android:completionThreshold="1"
|
||||
android:hint="POI tag" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/buttonSetPOITag"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="right"
|
||||
android:layout_weight="0"
|
||||
android:text="Search" />
|
||||
</LinearLayout>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/routeInfo"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="#FFBBBBBB"
|
||||
android:textColor="#FF000000" />
|
||||
|
||||
<RelativeLayout
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="fill_parent" >
|
||||
|
||||
<org.osmdroid.views.MapView
|
||||
android:id="@+id/map"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="fill_parent" />
|
||||
</RelativeLayout>
|
||||
|
||||
</LinearLayout>
|
||||
15
res/menu/map_menu.xml
Normal file
@ -0,0 +1,15 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<menu xmlns:android="http://schemas.android.com/apk/res/android" >
|
||||
<item
|
||||
android:id="@+id/menu_departure"
|
||||
android:title="Set Departure"/>
|
||||
<item
|
||||
android:id="@+id/menu_destination"
|
||||
android:title="Set Destination"/>
|
||||
<item
|
||||
android:id="@+id/menu_viapoint"
|
||||
android:title="Add Via-Point"/>
|
||||
<item
|
||||
android:id="@+id/menu_none"
|
||||
android:title="Cancel"/>
|
||||
</menu>
|
||||
@ -13,9 +13,11 @@
|
||||
<item
|
||||
android:id="@+id/menu_position_my_location_disable"
|
||||
android:title="@string/menu_position_my_location_disable"/>
|
||||
<!-- <item
|
||||
<!--
|
||||
<item
|
||||
android:id="@+id/menu_position_last_known"
|
||||
android:title="@string/menu_position_last_known"/> -->
|
||||
android:title="@string/menu_position_last_known"/>
|
||||
-->
|
||||
<item
|
||||
android:id="@+id/menu_position_enter_coordinates"
|
||||
android:title="@string/menu_position_enter_coordinates"/>
|
||||
@ -28,57 +30,73 @@
|
||||
<item
|
||||
android:id="@+id/menu_rotation_disable"
|
||||
android:title="@string/menu_rotation_disable"/>
|
||||
<item
|
||||
<item
|
||||
android:id="@+id/menu_compass_enable"
|
||||
android:title="@string/menu_compass_enable"/>
|
||||
<item
|
||||
<item
|
||||
android:id="@+id/menu_compass_disable"
|
||||
android:title="@string/menu_compass_disable"/>
|
||||
</menu>
|
||||
</item>
|
||||
<item
|
||||
<!--
|
||||
<item
|
||||
android:id="@+id/menu_options"
|
||||
android:icon="@drawable/ic_menu_options"
|
||||
android:showAsAction="always"
|
||||
android:title="@string/menu_options">
|
||||
<menu>
|
||||
-->
|
||||
<!-- android:icon="@drawable/ic_menu_preferences" -->
|
||||
<item
|
||||
android:id="@+id/menu_preferences"
|
||||
android:showAsAction="never"
|
||||
android:icon="@drawable/ic_menu_options"
|
||||
android:title="@string/menu_preferences"/>
|
||||
|
||||
<menu>
|
||||
<!-- android:icon="@drawable/ic_menu_preferences" -->
|
||||
<item
|
||||
android:id="@+id/menu_preferences"
|
||||
|
||||
android:showAsAction="never"
|
||||
android:title="@string/menu_preferences"/>
|
||||
<!-- android:icon="@drawable/ic_menu_mapmode" -->
|
||||
<item
|
||||
android:id="@+id/menu_render_theme"
|
||||
|
||||
android:showAsAction="never"
|
||||
android:title="@string/menu_render_theme">
|
||||
<menu>
|
||||
<item
|
||||
android:id="@+id/menu_render_theme_osmarender"
|
||||
android:title="@string/menu_render_theme_osmarender"/>
|
||||
<item
|
||||
android:id="@+id/menu_render_theme_tronrender"
|
||||
android:title="@string/menu_render_theme_tronrender"/>
|
||||
<item
|
||||
android:id="@+id/menu_render_theme_select_file"
|
||||
android:title="@string/menu_render_theme_select_file"/>
|
||||
</menu>
|
||||
</item>
|
||||
<!-- android:icon="@drawable/ic_menu_archive" -->
|
||||
<item
|
||||
android:id="@+id/menu_mapfile"
|
||||
|
||||
android:showAsAction="never"
|
||||
android:title="@string/menu_mapfile"/>
|
||||
<item
|
||||
android:id="@+id/menu_info_about"
|
||||
android:title="@string/menu_info_about"/>
|
||||
<!-- android:icon="@drawable/ic_menu_mapmode" -->
|
||||
<!-- android:icon="@drawable/ic_menu_archive" -->
|
||||
<!--
|
||||
<item
|
||||
android:id="@+id/menu_mapfile"
|
||||
android:showAsAction="never"
|
||||
android:title="@string/menu_mapfile"/>
|
||||
-->
|
||||
<!--
|
||||
<item
|
||||
android:id="@+id/menu_info_map_file"
|
||||
android:title="@string/menu_info_map_file"/>
|
||||
-->
|
||||
<!--
|
||||
</menu>
|
||||
|
||||
</item>
|
||||
|
||||
-->
|
||||
|
||||
<item
|
||||
android:id="@+id/menu_render_theme"
|
||||
android:showAsAction="never"
|
||||
android:title="@string/menu_render_theme">
|
||||
<menu>
|
||||
<item
|
||||
android:id="@+id/menu_render_theme_osmarender"
|
||||
android:title="@string/menu_render_theme_osmarender"/>
|
||||
<item
|
||||
android:id="@+id/menu_render_theme_tronrender"
|
||||
android:title="@string/menu_render_theme_tronrender"/>
|
||||
<item
|
||||
android:id="@+id/menu_render_theme_select_file"
|
||||
android:title="@string/menu_render_theme_select_file"/>
|
||||
</menu>
|
||||
</item>
|
||||
<item
|
||||
android:id="@+id/menu_info_about"
|
||||
android:title="@string/menu_info_about"/>
|
||||
|
||||
<item
|
||||
android:id="@+id/menu_pois"
|
||||
android:title="@string/menu_pois"/>
|
||||
|
||||
<item
|
||||
android:id="@+id/menu_poi_list"
|
||||
android:title="@string/menu_poi_list"/>
|
||||
|
||||
</menu>
|
||||
@ -1,94 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<menu xmlns:android="http://schemas.android.com/apk/res/android" >
|
||||
|
||||
<item
|
||||
android:id="@+id/menu_position"
|
||||
android:icon="@drawable/ic_menu_mylocation"
|
||||
android:showAsAction="always"
|
||||
android:title="@string/menu_position">
|
||||
<menu>
|
||||
<item
|
||||
android:id="@+id/menu_position_my_location_enable"
|
||||
android:title="@string/menu_position_my_location_enable"/>
|
||||
<item
|
||||
android:id="@+id/menu_position_my_location_disable"
|
||||
android:title="@string/menu_position_my_location_disable"/>
|
||||
<!--
|
||||
<item
|
||||
android:id="@+id/menu_position_last_known"
|
||||
android:title="@string/menu_position_last_known"/>
|
||||
-->
|
||||
<item
|
||||
android:id="@+id/menu_position_enter_coordinates"
|
||||
android:title="@string/menu_position_enter_coordinates"/>
|
||||
<item
|
||||
android:id="@+id/menu_position_map_center"
|
||||
android:title="@string/menu_position_map_file_center"/>
|
||||
<item
|
||||
android:id="@+id/menu_rotation_enable"
|
||||
android:title="@string/menu_rotation_enable"/>
|
||||
<item
|
||||
android:id="@+id/menu_rotation_disable"
|
||||
android:title="@string/menu_rotation_disable"/>
|
||||
<item
|
||||
android:id="@+id/menu_compass_enable"
|
||||
android:title="@string/menu_compass_enable"/>
|
||||
<item
|
||||
android:id="@+id/menu_compass_disable"
|
||||
android:title="@string/menu_compass_disable"/>
|
||||
</menu>
|
||||
</item>
|
||||
<!--
|
||||
<item
|
||||
android:id="@+id/menu_options"
|
||||
android:icon="@drawable/ic_menu_options"
|
||||
android:showAsAction="always"
|
||||
android:title="@string/menu_options">
|
||||
<menu>
|
||||
-->
|
||||
<!-- android:icon="@drawable/ic_menu_preferences" -->
|
||||
<item
|
||||
android:id="@+id/menu_preferences"
|
||||
android:showAsAction="never"
|
||||
android:icon="@drawable/ic_menu_options"
|
||||
android:title="@string/menu_preferences"/>
|
||||
|
||||
<!-- android:icon="@drawable/ic_menu_mapmode" -->
|
||||
<!-- android:icon="@drawable/ic_menu_archive" -->
|
||||
<!--
|
||||
<item
|
||||
android:id="@+id/menu_mapfile"
|
||||
android:showAsAction="never"
|
||||
android:title="@string/menu_mapfile"/>
|
||||
-->
|
||||
<!--
|
||||
<item
|
||||
android:id="@+id/menu_info_map_file"
|
||||
android:title="@string/menu_info_map_file"/>
|
||||
-->
|
||||
<!--
|
||||
</menu>
|
||||
</item>
|
||||
-->
|
||||
|
||||
<item
|
||||
android:id="@+id/menu_render_theme"
|
||||
android:showAsAction="never"
|
||||
android:title="@string/menu_render_theme">
|
||||
<menu>
|
||||
<item
|
||||
android:id="@+id/menu_render_theme_osmarender"
|
||||
android:title="@string/menu_render_theme_osmarender"/>
|
||||
<item
|
||||
android:id="@+id/menu_render_theme_tronrender"
|
||||
android:title="@string/menu_render_theme_tronrender"/>
|
||||
<item
|
||||
android:id="@+id/menu_render_theme_select_file"
|
||||
android:title="@string/menu_render_theme_select_file"/>
|
||||
</menu>
|
||||
</item>
|
||||
<item
|
||||
android:id="@+id/menu_info_about"
|
||||
android:title="@string/menu_info_about"/>
|
||||
|
||||
</menu>
|
||||
10
res/menu/poi_menu.xml
Normal file
@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<menu xmlns:android="http://schemas.android.com/apk/res/android" >
|
||||
|
||||
<item
|
||||
android:id="@+id/menu_link"
|
||||
android:title="Open URL"/>
|
||||
<item
|
||||
android:id="@+id/menu_none"
|
||||
android:title="Cancel"/>
|
||||
</menu>
|
||||
101
res/values/poi_tags.xml
Normal file
@ -0,0 +1,101 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<resources>
|
||||
<!-- OSM tags relevant for a Nominatim search by feature -->
|
||||
<!-- See http://wiki.openstreetmap.org/wiki/Map_Features -->
|
||||
<!-- in comment, those with 0 match for Paris area on 16/07/2012 -->
|
||||
<string-array name="poi_tags">
|
||||
<!-- Aeroway -->
|
||||
<!-- <item>aerodrome</item> -->
|
||||
<!-- Amenity -->
|
||||
<item>bar</item>
|
||||
<item>cafe</item>
|
||||
<!-- <item>fast_food</item> -->
|
||||
<item>pub</item>
|
||||
<item>restaurant</item>
|
||||
<item>library</item>
|
||||
<item>school</item>
|
||||
<item>university</item>
|
||||
<!-- <item>bicycle_parking</item> -->
|
||||
<item>bus_station</item>
|
||||
<!-- <item>car_rental</item> -->
|
||||
<item>car_sharing</item>
|
||||
<item>car_wash</item>
|
||||
<!-- <item>ev_charging</item> -->
|
||||
<item>fuel</item>
|
||||
<item>parking</item>
|
||||
<item>taxi</item>
|
||||
<item>atm</item>
|
||||
<item>bank</item>
|
||||
<item>dentist</item>
|
||||
<item>hospital</item>
|
||||
<item>pharmacy</item>
|
||||
<!-- <item>veterinary</item> -->
|
||||
<item>cinema</item>
|
||||
<!-- <item>nightclub</item> -->
|
||||
<item>theatre</item>
|
||||
<!-- <item>fire_station</item> -->
|
||||
<item>marketplace</item>
|
||||
<item>police</item>
|
||||
<!-- <item>post_box</item> -->
|
||||
<!-- <item>post_office</item> -->
|
||||
<!-- <item>recycling</item> -->
|
||||
<!-- <item>sauna</item> -->
|
||||
<item>telephone</item>
|
||||
<item>toilets</item>
|
||||
<!-- Building -->
|
||||
<item>hotel</item>
|
||||
<item>church</item>
|
||||
<!-- <item>train_station</item> => station prefered -->
|
||||
<!-- Craft -->
|
||||
<!-- Emergency -->
|
||||
<!-- <item>ambulance_station</item> -->
|
||||
<!-- <item>phone</item> -->
|
||||
<!-- Highway -->
|
||||
<!-- <item>speed_camera</item> -->
|
||||
<!-- Historic -->
|
||||
<item>castle</item>
|
||||
<item>monument</item>
|
||||
<!-- Land use -->
|
||||
<item>cemetery</item>
|
||||
<!-- Leisure -->
|
||||
<!-- <item>dance</item> -->
|
||||
<!-- <item>golf_course</item> -->
|
||||
<item>park</item>
|
||||
<item>stadium</item>
|
||||
<!-- <item>swimming_pool</item> -->
|
||||
<!-- Natural -->
|
||||
<item>beach</item>
|
||||
<!-- Public Transport -->
|
||||
<item>station</item>
|
||||
<!-- Shop -->
|
||||
<item>bakery</item>
|
||||
<item>butcher</item>
|
||||
<!-- <item>car_repair</item> -->
|
||||
<!-- <item>clothes</item> -->
|
||||
<!-- <item>convenience</item> -->
|
||||
<item>florist</item>
|
||||
<item>hairdresser</item>
|
||||
<!-- <item>jewelry</item> -->
|
||||
<item>kiosk</item>
|
||||
<item>laundry</item>
|
||||
<item>mall</item>
|
||||
<item>optician</item>
|
||||
<!-- <item>shoes</item> -->
|
||||
<item>supermarket</item>
|
||||
<!-- Sport -->
|
||||
<!-- <item>golf</item> -->
|
||||
<!-- <item>horse_racing</item> -->
|
||||
<!-- <item>judo</item> -->
|
||||
<!-- <item>karting</item> -->
|
||||
<!-- <item>tennis</item> -->
|
||||
<!-- Tourism -->
|
||||
<item>information</item>
|
||||
<item>museum</item>
|
||||
<item>viewpoint</item>
|
||||
<item>zoo</item>
|
||||
<!-- Non-Nominatim tags, for other services: -->
|
||||
<item>wikipedia</item>
|
||||
<item>flickr</item>
|
||||
<item>picasa</item>
|
||||
</string-array>
|
||||
</resources>
|
||||
@ -2,12 +2,13 @@
|
||||
<resources>
|
||||
|
||||
<string-array name="preferences_map_generator_values">
|
||||
|
||||
<!-- <item>Mapfile</item> -->
|
||||
<!-- <item>PostGIS</item> -->
|
||||
<item>OpenScienceMap</item>
|
||||
<item>OpenScienceMap2</item>
|
||||
<item>OpenScienceMap2</item>
|
||||
<item>Test</item>
|
||||
</string-array>
|
||||
</string-array>
|
||||
<string-array name="preferences_scale_bar_unit_values">
|
||||
<item>Imperial</item>
|
||||
<item>Metric</item>
|
||||
@ -111,5 +112,9 @@
|
||||
<string name="unit_symbol_meter"> m</string>
|
||||
<string name="zoomLevel">Zoom level</string>
|
||||
<string name="menu_options">Options</string>
|
||||
|
||||
<string name="menu_pois">Bars nearby</string>
|
||||
<string name="menu_poi_list">List POIs</string>
|
||||
<string name="departure">Departure</string>
|
||||
<string name="destination">Destination</string>
|
||||
<string name="viapoint">Via-Point</string>
|
||||
</resources>
|
||||
38
src/org/oscim/app/App.java
Normal file
@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Copyright 2012 Hannes Janetzek
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it under the
|
||||
* terms of the GNU Lesser 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 Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.oscim.app;
|
||||
|
||||
import org.oscim.view.MapView;
|
||||
|
||||
import android.app.Application;
|
||||
import android.content.res.Resources;
|
||||
|
||||
// see http://androidcookbook.com : Android's Application Object as a "Singleton"
|
||||
public class App extends Application {
|
||||
|
||||
public static final String TAG = "TileMap";
|
||||
static MapView map;
|
||||
static POISearch poiSearch;
|
||||
static Resources res;
|
||||
|
||||
@Override
|
||||
public void onCreate() {
|
||||
super.onCreate();
|
||||
res = getResources();
|
||||
// Log.d("...", ">>>>>>>> INIT <<<<<<<");
|
||||
// sInstance = this;
|
||||
// sInstance.initializeInstance();
|
||||
}
|
||||
}
|
||||
113
src/org/oscim/app/LocationDialog.java
Normal file
@ -0,0 +1,113 @@
|
||||
/*
|
||||
* Copyright 2010, 2011, 2012 mapsforge.org
|
||||
* Copyright 2012 Hannes Janetzek
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it under the
|
||||
* terms of the GNU Lesser 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 Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.oscim.app;
|
||||
|
||||
import org.oscim.core.GeoPoint;
|
||||
import org.oscim.core.MapPosition;
|
||||
import org.oscim.view.MapView;
|
||||
|
||||
import android.app.AlertDialog;
|
||||
import android.app.Dialog;
|
||||
import android.content.DialogInterface;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.widget.EditText;
|
||||
import android.widget.SeekBar;
|
||||
import android.widget.TextView;
|
||||
|
||||
public class LocationDialog {
|
||||
|
||||
void prepareDialog(MapView mapView, final Dialog dialog) {
|
||||
EditText editText = (EditText) dialog.findViewById(R.id.latitude);
|
||||
GeoPoint mapCenter = mapView.getMapPosition().getMapCenter();
|
||||
editText.setText(Double.toString(mapCenter.getLatitude()));
|
||||
|
||||
editText = (EditText) dialog.findViewById(R.id.longitude);
|
||||
editText.setText(Double.toString(mapCenter.getLongitude()));
|
||||
|
||||
SeekBar zoomlevel = (SeekBar) dialog.findViewById(R.id.zoomLevel);
|
||||
zoomlevel.setMax(20); // FIXME
|
||||
// map.getMapGenerator().getZoomLevelMax());
|
||||
zoomlevel.setProgress(mapView.getMapPosition().getZoomLevel());
|
||||
|
||||
final TextView textView = (TextView) dialog.findViewById(R.id.zoomlevelValue);
|
||||
textView.setText(String.valueOf(zoomlevel.getProgress()));
|
||||
zoomlevel.setOnSeekBarChangeListener(new SeekBarChangeListener(textView));
|
||||
}
|
||||
|
||||
Dialog createDialog(final TileMap map) {
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(map);
|
||||
builder.setIcon(android.R.drawable.ic_menu_mylocation);
|
||||
builder.setTitle(R.string.menu_position_enter_coordinates);
|
||||
LayoutInflater factory = LayoutInflater.from(map);
|
||||
final View view = factory.inflate(R.layout.dialog_enter_coordinates, null);
|
||||
builder.setView(view);
|
||||
|
||||
builder.setPositiveButton(R.string.go_to_position,
|
||||
new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
// disable GPS follow mode if it is enabled
|
||||
map.mLocation.disableSnapToLocation(true);
|
||||
|
||||
// set the map center and zoom level
|
||||
EditText latitudeView = (EditText) view
|
||||
.findViewById(R.id.latitude);
|
||||
EditText longitudeView = (EditText) view
|
||||
.findViewById(R.id.longitude);
|
||||
double latitude = Double.parseDouble(latitudeView.getText()
|
||||
.toString());
|
||||
double longitude = Double.parseDouble(longitudeView.getText()
|
||||
.toString());
|
||||
|
||||
SeekBar zoomLevelView = (SeekBar) view
|
||||
.findViewById(R.id.zoomLevel);
|
||||
|
||||
byte zoom = (byte) (zoomLevelView.getProgress());
|
||||
|
||||
MapPosition mapPosition = new MapPosition(latitude,
|
||||
longitude, zoom, 1, 0);
|
||||
|
||||
map.map.setMapCenter(mapPosition);
|
||||
}
|
||||
});
|
||||
builder.setNegativeButton(R.string.cancel, null);
|
||||
return builder.create();
|
||||
}
|
||||
|
||||
class SeekBarChangeListener implements SeekBar.OnSeekBarChangeListener {
|
||||
private final TextView textView;
|
||||
|
||||
SeekBarChangeListener(TextView textView) {
|
||||
this.textView = textView;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
|
||||
this.textView.setText(String.valueOf(progress));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStartTrackingTouch(SeekBar seekBar) {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStopTrackingTouch(SeekBar seekBar) {
|
||||
// do nothing
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -107,14 +107,14 @@ public class LocationHandler {
|
||||
}
|
||||
|
||||
if (bestLocation != null) {
|
||||
byte zoom = mTileMap.mMapView.getMapPosition().getZoomLevel();
|
||||
byte zoom = mTileMap.map.getMapPosition().getZoomLevel();
|
||||
if (zoom < 12)
|
||||
zoom = (byte) 12;
|
||||
|
||||
MapPosition mapPosition = new MapPosition(bestLocation.getLatitude(),
|
||||
bestLocation.getLongitude(), zoom, 1, 0);
|
||||
|
||||
mTileMap.mMapView.setMapCenter(mapPosition);
|
||||
mTileMap.map.setMapCenter(mapPosition);
|
||||
|
||||
} else {
|
||||
mTileMap.showToastOnUiThread(mTileMap
|
||||
@ -167,7 +167,7 @@ public class LocationHandler {
|
||||
mSnapToLocation = false;
|
||||
mSnapToLocationView.setChecked(false);
|
||||
|
||||
mTileMap.mMapView.setClickable(true);
|
||||
mTileMap.map.setClickable(true);
|
||||
|
||||
if (showToast) {
|
||||
mTileMap.showToastOnUiThread(mTileMap
|
||||
@ -186,7 +186,7 @@ public class LocationHandler {
|
||||
if (!mSnapToLocation) {
|
||||
mSnapToLocation = true;
|
||||
|
||||
mTileMap.mMapView.setClickable(false);
|
||||
mTileMap.map.setClickable(false);
|
||||
|
||||
if (showToast) {
|
||||
mTileMap.showToastOnUiThread(mTileMap
|
||||
@ -228,7 +228,7 @@ public class LocationHandler {
|
||||
|
||||
if (mSetCenter || isSnapToLocationEnabled()) {
|
||||
mSetCenter = false;
|
||||
mTileMap.mMapView.setCenter(point);
|
||||
mTileMap.map.setCenter(point);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
96
src/org/oscim/app/MapInfoDialog.java
Normal file
@ -0,0 +1,96 @@
|
||||
/*
|
||||
* Copyright 2010, 2011, 2012 mapsforge.org
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it under the
|
||||
* terms of the GNU Lesser 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 Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.oscim.app;
|
||||
|
||||
|
||||
public class MapInfoDialog {
|
||||
//onCreate
|
||||
// else if (id == DIALOG_INFO_MAP_FILE) {
|
||||
// builder.setIcon(android.R.drawable.ic_menu_info_details);
|
||||
// builder.setTitle(R.string.menu_info_map_file);
|
||||
// LayoutInflater factory = LayoutInflater.from(this);
|
||||
// builder.setView(factory.inflate(R.layout.dialog_info_map_file, null));
|
||||
// builder.setPositiveButton(R.string.ok, null);
|
||||
// return builder.create();
|
||||
|
||||
//onPrepare
|
||||
// } else if (id == DIALOG_INFO_MAP_FILE) {
|
||||
// MapInfo mapInfo = map.getMapDatabase().getMapInfo();
|
||||
//
|
||||
// TextView textView = (TextView)
|
||||
// dialog.findViewById(R.id.infoMapFileViewName);
|
||||
// textView.setText(map.getMapFile());
|
||||
//
|
||||
// textView = (TextView)
|
||||
// dialog.findViewById(R.id.infoMapFileViewSize);
|
||||
// textView.setText(FileUtils.formatFileSize(mapInfo.fileSize,
|
||||
// getResources()));
|
||||
//
|
||||
// textView = (TextView)
|
||||
// dialog.findViewById(R.id.infoMapFileViewVersion);
|
||||
// textView.setText(String.valueOf(mapInfo.fileVersion));
|
||||
//
|
||||
// // textView = (TextView)
|
||||
// dialog.findViewById(R.id.infoMapFileViewDebug);
|
||||
// // if (mapFileInfo.debugFile) {
|
||||
// // textView.setText(R.string.info_map_file_debug_yes);
|
||||
// // } else {
|
||||
// // textView.setText(R.string.info_map_file_debug_no);
|
||||
// // }
|
||||
//
|
||||
// textView = (TextView)
|
||||
// dialog.findViewById(R.id.infoMapFileViewDate);
|
||||
// Date date = new Date(mapInfo.mapDate);
|
||||
// textView.setText(DateFormat.getDateTimeInstance().format(date));
|
||||
//
|
||||
// textView = (TextView)
|
||||
// dialog.findViewById(R.id.infoMapFileViewArea);
|
||||
// BoundingBox boundingBox = mapInfo.boundingBox;
|
||||
// textView.setText(boundingBox.getMinLatitude() + ", "
|
||||
// + boundingBox.getMinLongitude() + " - \n"
|
||||
// + boundingBox.getMaxLatitude() + ", " +
|
||||
// boundingBox.getMaxLongitude());
|
||||
//
|
||||
// textView = (TextView)
|
||||
// dialog.findViewById(R.id.infoMapFileViewStartPosition);
|
||||
// GeoPoint startPosition = mapInfo.startPosition;
|
||||
// if (startPosition == null) {
|
||||
// textView.setText(null);
|
||||
// } else {
|
||||
// textView.setText(startPosition.getLatitude() + ", "
|
||||
// + startPosition.getLongitude());
|
||||
// }
|
||||
//
|
||||
// textView = (TextView)
|
||||
// dialog.findViewById(R.id.infoMapFileViewStartZoomLevel);
|
||||
// Byte startZoomLevel = mapInfo.startZoomLevel;
|
||||
// if (startZoomLevel == null) {
|
||||
// textView.setText(null);
|
||||
// } else {
|
||||
// textView.setText(startZoomLevel.toString());
|
||||
// }
|
||||
//
|
||||
// textView = (TextView) dialog
|
||||
// .findViewById(R.id.infoMapFileViewLanguagePreference);
|
||||
// textView.setText(mapInfo.languagePreference);
|
||||
//
|
||||
// textView = (TextView)
|
||||
// dialog.findViewById(R.id.infoMapFileViewComment);
|
||||
// textView.setText(mapInfo.comment);
|
||||
//
|
||||
// textView = (TextView)
|
||||
// dialog.findViewById(R.id.infoMapFileViewCreatedBy);
|
||||
// textView.setText(mapInfo.createdBy);
|
||||
}
|
||||
261
src/org/oscim/app/POIActivity.java
Normal file
@ -0,0 +1,261 @@
|
||||
/*
|
||||
* Copyright 2012 osmdroidbonuspack: M.Kergall
|
||||
* Copyright 2012 Hannes Janetzek
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it under the
|
||||
* terms of the GNU Lesser 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 Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.oscim.app;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
import org.osmdroid.location.POI;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.util.Log;
|
||||
import android.view.ContextMenu;
|
||||
import android.view.ContextMenu.ContextMenuInfo;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.MenuInflater;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.view.View.OnClickListener;
|
||||
import android.view.View.OnCreateContextMenuListener;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.inputmethod.InputMethodManager;
|
||||
import android.widget.AdapterView;
|
||||
import android.widget.AdapterView.OnItemClickListener;
|
||||
import android.widget.ArrayAdapter;
|
||||
import android.widget.AutoCompleteTextView;
|
||||
import android.widget.BaseAdapter;
|
||||
import android.widget.Button;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.ListView;
|
||||
import android.widget.TextView;
|
||||
|
||||
/**
|
||||
* Activity showing POIs as a list.
|
||||
* @author M.Kergall
|
||||
*/
|
||||
|
||||
public class POIActivity extends Activity {
|
||||
|
||||
AutoCompleteTextView poiTagText;
|
||||
POIAdapter mAdapter;
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.items_list);
|
||||
|
||||
// TextView title = (TextView) findViewById(R.id.title);
|
||||
// title.setText("Points of Interest");
|
||||
|
||||
ListView list = (ListView) findViewById(R.id.items);
|
||||
|
||||
Intent myIntent = getIntent();
|
||||
|
||||
final ArrayList<POI> pois = myIntent.getParcelableArrayListExtra("POI");
|
||||
final int currentNodeId = myIntent.getIntExtra("ID", -1);
|
||||
POIAdapter adapter = new POIAdapter(this, pois);
|
||||
mAdapter = adapter;
|
||||
// Log.d(App.TAG, "got POIs:" + (pois == null ? pois : pois.size()));
|
||||
|
||||
list.setOnItemClickListener(new OnItemClickListener() {
|
||||
@Override
|
||||
public void onItemClick(AdapterView<?> arg0, View view, int position, long index) {
|
||||
Intent intent = new Intent();
|
||||
intent.putExtra("ID", position);
|
||||
setResult(RESULT_OK, intent);
|
||||
finish();
|
||||
}
|
||||
});
|
||||
|
||||
list.setOnCreateContextMenuListener(new OnCreateContextMenuListener() {
|
||||
|
||||
@Override
|
||||
public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) {
|
||||
// TODO Auto-generated method stub
|
||||
|
||||
Log.d(App.TAG, "context menu created 2");
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
list.setAdapter(adapter);
|
||||
list.setSelection(currentNodeId);
|
||||
|
||||
// POI search interface:
|
||||
String[] poiTags = getResources().getStringArray(R.array.poi_tags);
|
||||
poiTagText = (AutoCompleteTextView) findViewById(R.id.poiTag);
|
||||
ArrayAdapter<String> textadapter = new ArrayAdapter<String>(this,
|
||||
android.R.layout.simple_dropdown_item_1line, poiTags);
|
||||
poiTagText.setAdapter(textadapter);
|
||||
|
||||
Button setPOITagButton = (Button) findViewById(R.id.buttonSetPOITag);
|
||||
setPOITagButton.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
//Hide the soft keyboard:
|
||||
InputMethodManager imm = (InputMethodManager)
|
||||
getSystemService(Context.INPUT_METHOD_SERVICE);
|
||||
imm.hideSoftInputFromWindow(poiTagText.getWindowToken(), 0);
|
||||
//Start search:
|
||||
App.poiSearch.getPOIAsync(poiTagText.getText().toString());
|
||||
}
|
||||
});
|
||||
|
||||
registerForContextMenu(list);
|
||||
|
||||
// only show keyboard when nothing in the list yet
|
||||
if (pois == null || pois.size() == 0) {
|
||||
poiTagText.postDelayed(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
InputMethodManager keyboard = (InputMethodManager)
|
||||
getSystemService(Context.INPUT_METHOD_SERVICE);
|
||||
keyboard.showSoftInput(poiTagText, 0);
|
||||
}
|
||||
}, 200);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onNewIntent(Intent intent) {
|
||||
Log.d(App.TAG, "NEW INTENT!!!!");
|
||||
// from SearchableDictionary Example:
|
||||
// Because this activity has set launchMode="singleTop", the system calls this method
|
||||
// to deliver the intent if this activity is currently the foreground activity when
|
||||
// invoked again (when the user executes a search from this activity, we don't create
|
||||
// a new instance of this activity, so the system delivers the search intent here)
|
||||
// handleIntent(intent);
|
||||
|
||||
final ArrayList<POI> pois = intent.getParcelableArrayListExtra("POI");
|
||||
// final int currentNodeId = intent.getIntExtra("ID", -1);
|
||||
// POIAdapter adapter = new POIAdapter(this, pois);
|
||||
mAdapter.setPOI(pois);
|
||||
mAdapter.notifyDataSetChanged();
|
||||
}
|
||||
|
||||
// http://www.mikeplate.com/2010/01/21/show-a-context-menu-for-long-clicks-in-an-android-listview/
|
||||
@Override
|
||||
public void onCreateContextMenu(ContextMenu menu, View v,
|
||||
ContextMenuInfo menuInfo) {
|
||||
if (v.getId() == R.id.items) {
|
||||
AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) menuInfo;
|
||||
Log.d(App.TAG, "list context menu created " + info.position);
|
||||
|
||||
MenuInflater inflater = getMenuInflater();
|
||||
inflater.inflate(R.menu.poi_menu, menu);
|
||||
|
||||
}
|
||||
|
||||
super.onCreateContextMenu(menu, v, menuInfo);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onContextItemSelected(MenuItem item) {
|
||||
Log.d(App.TAG, "context menu item selected " + item.getItemId());
|
||||
|
||||
if (item.getItemId() == R.id.menu_link) {
|
||||
|
||||
AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) item
|
||||
.getMenuInfo();
|
||||
|
||||
POI poi = (POI) mAdapter.getItem(info.position);
|
||||
if (poi != null && poi.url != null) {
|
||||
|
||||
Intent i = new Intent(Intent.ACTION_VIEW);
|
||||
i.setData(Uri.parse(poi.url));
|
||||
startActivity(i);
|
||||
|
||||
}
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
return super.onContextItemSelected(item);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class POIAdapter extends BaseAdapter implements OnClickListener {
|
||||
private Context mContext;
|
||||
private ArrayList<POI> mPois;
|
||||
|
||||
public POIAdapter(Context context, ArrayList<POI> pois) {
|
||||
mContext = context;
|
||||
mPois = pois;
|
||||
}
|
||||
|
||||
public void setPOI(ArrayList<POI> pois) {
|
||||
mPois = pois;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCount() {
|
||||
if (mPois == null)
|
||||
return 0;
|
||||
|
||||
return mPois.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getItem(int position) {
|
||||
if (mPois == null)
|
||||
return null;
|
||||
|
||||
return mPois.get(position);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getItemId(int position) {
|
||||
return position;
|
||||
}
|
||||
|
||||
@Override
|
||||
public View getView(int position, View convertView, ViewGroup viewGroup) {
|
||||
POI entry = (POI) getItem(position);
|
||||
if (convertView == null) {
|
||||
LayoutInflater inflater = (LayoutInflater) mContext
|
||||
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
|
||||
convertView = inflater.inflate(R.layout.item_layout, null);
|
||||
} else {
|
||||
|
||||
}
|
||||
|
||||
TextView tvTitle = (TextView) convertView.findViewById(R.id.title);
|
||||
tvTitle.setText((entry.url == null ? "" : "[link] ") + entry.type);
|
||||
TextView tvDetails = (TextView) convertView.findViewById(R.id.details);
|
||||
tvDetails.setText(entry.description);
|
||||
|
||||
ImageView iv = (ImageView) convertView.findViewById(R.id.thumbnail);
|
||||
//ivManeuver.setImageBitmap(entry.mThumbnail);
|
||||
// iv.getT
|
||||
// entry.fetchThumbnailOnThread(iv);
|
||||
entry.fetchThumbnail(iv);
|
||||
|
||||
return convertView;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClick(View arg0) {
|
||||
Log.d(App.TAG, "click" + arg0.getId());
|
||||
//nothing to do.
|
||||
}
|
||||
|
||||
}
|
||||
197
src/org/oscim/app/POISearch.java
Normal file
@ -0,0 +1,197 @@
|
||||
/*
|
||||
* Copyright 2012 osmdroid: M.Kergall
|
||||
* Copyright 2012 Hannes Janetzek
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it under the
|
||||
* terms of the GNU Lesser 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 Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.oscim.app;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
import org.oscim.core.BoundingBox;
|
||||
import org.oscim.overlay.OverlayItem;
|
||||
import org.osmdroid.location.FlickrPOIProvider;
|
||||
import org.osmdroid.location.GeoNamesPOIProvider;
|
||||
import org.osmdroid.location.NominatimPOIProvider;
|
||||
import org.osmdroid.location.POI;
|
||||
import org.osmdroid.location.PicasaPOIProvider;
|
||||
import org.osmdroid.overlays.ExtendedOverlayItem;
|
||||
import org.osmdroid.overlays.ItemizedOverlayWithBubble;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.os.AsyncTask;
|
||||
import android.util.Log;
|
||||
import android.widget.AutoCompleteTextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
public class POISearch {
|
||||
ArrayList<POI> mPOIs;
|
||||
ItemizedOverlayWithBubble<ExtendedOverlayItem> poiMarkers;
|
||||
AutoCompleteTextView poiTagText;
|
||||
|
||||
final TileMap tileMap;
|
||||
|
||||
POISearch(TileMap tileMap) {
|
||||
this.tileMap = tileMap;
|
||||
//POI markers:
|
||||
final ArrayList<ExtendedOverlayItem> poiItems = new ArrayList<ExtendedOverlayItem>();
|
||||
poiMarkers = new ItemizedOverlayWithBubble<ExtendedOverlayItem>(tileMap.map, tileMap,
|
||||
poiItems, null); //new POIInfoWindow(map));
|
||||
|
||||
tileMap.map.getOverlays().add(poiMarkers);
|
||||
|
||||
// if (savedInstanceState != null) {
|
||||
// mPOIs = savedInstanceState.getParcelableArrayList("poi");
|
||||
// updateUIWithPOI(mPOIs);
|
||||
// }
|
||||
}
|
||||
|
||||
// void init() {
|
||||
//
|
||||
// }
|
||||
|
||||
class POITask extends AsyncTask<Object, Void, ArrayList<POI>> {
|
||||
String mTag;
|
||||
|
||||
@Override
|
||||
protected ArrayList<POI> doInBackground(Object... params) {
|
||||
mTag = (String) params[0];
|
||||
|
||||
if (mTag == null || mTag.equals("")) {
|
||||
return null;
|
||||
} else if (mTag.equals("wikipedia")) {
|
||||
GeoNamesPOIProvider poiProvider = new GeoNamesPOIProvider("mkergall");
|
||||
//ArrayList<POI> pois = poiProvider.getPOICloseTo(point, 30, 20.0);
|
||||
//Get POI inside the bounding box of the current map view:
|
||||
BoundingBox bb = App.map.getBoundingBox();
|
||||
ArrayList<POI> pois = poiProvider.getPOIInside(bb, 30);
|
||||
return pois;
|
||||
} else if (mTag.equals("flickr")) {
|
||||
FlickrPOIProvider poiProvider = new FlickrPOIProvider(
|
||||
"c39be46304a6c6efda8bc066c185cd7e");
|
||||
BoundingBox bb = App.map.getBoundingBox();
|
||||
ArrayList<POI> pois = poiProvider.getPOIInside(bb, 20);
|
||||
return pois;
|
||||
} else if (mTag.startsWith("picasa")) {
|
||||
PicasaPOIProvider poiProvider = new PicasaPOIProvider(null);
|
||||
BoundingBox bb = App.map.getBoundingBox();
|
||||
String q = mTag.substring("picasa".length());
|
||||
ArrayList<POI> pois = poiProvider.getPOIInside(bb, 20, q);
|
||||
return pois;
|
||||
}
|
||||
|
||||
else {
|
||||
NominatimPOIProvider poiProvider = new NominatimPOIProvider();
|
||||
// poiProvider.setService(NominatimPOIProvider.MAPQUEST_POI_SERVICE);
|
||||
poiProvider.setService(NominatimPOIProvider.NOMINATIM_POI_SERVICE);
|
||||
ArrayList<POI> pois;
|
||||
// if (destinationPoint == null) {
|
||||
BoundingBox bb = App.map.getBoundingBox();
|
||||
pois = poiProvider.getPOIInside(bb, mTag, 100);
|
||||
// } else {
|
||||
// pois = poiProvider.getPOIAlong(mRoad.getRouteLow(), mTag, 100, 2.0);
|
||||
// }
|
||||
return pois;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(ArrayList<POI> pois) {
|
||||
mPOIs = pois;
|
||||
if (mTag.equals("")) {
|
||||
//no search, no message
|
||||
} else if (mPOIs == null) {
|
||||
Toast.makeText(tileMap.getApplicationContext(),
|
||||
"Technical issue when getting " + mTag + " POI.", Toast.LENGTH_LONG)
|
||||
.show();
|
||||
} else {
|
||||
Toast.makeText(tileMap.getApplicationContext(),
|
||||
"" + mPOIs.size() + " " + mTag + " entries found",
|
||||
Toast.LENGTH_LONG).show();
|
||||
// if (mTag.equals("flickr") || mTag.startsWith("picasa") || mTag.equals("wikipedia"))
|
||||
// startAsyncThumbnailsLoading(mPOIs);
|
||||
}
|
||||
updateUIWithPOI(mPOIs);
|
||||
}
|
||||
}
|
||||
|
||||
void updateUIWithPOI(ArrayList<POI> pois) {
|
||||
if (pois != null) {
|
||||
for (POI poi : pois) {
|
||||
int end = 0, first = 0;
|
||||
String desc = null;
|
||||
String name = poi.description;
|
||||
|
||||
if (poi.serviceId == POI.POI_SERVICE_NOMINATIM) {
|
||||
if (name != null) {
|
||||
// FIXME or nominatim...
|
||||
// String name = "";
|
||||
for (int i = 0; i < 3; i++) {
|
||||
int pos = poi.description.indexOf(',', end);
|
||||
if (pos > 0) {
|
||||
if (i == 0) {
|
||||
name = poi.description.substring(0, pos);
|
||||
first = pos + 2;
|
||||
}
|
||||
end = pos + 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (end > 0)
|
||||
desc = poi.description.substring(first, end - 1);
|
||||
else
|
||||
desc = poi.description;
|
||||
}
|
||||
}
|
||||
ExtendedOverlayItem poiMarker = new ExtendedOverlayItem(
|
||||
poi.type + (name == null ? "" : ": " + name), desc, poi.location);
|
||||
Drawable marker = null;
|
||||
|
||||
if (poi.serviceId == POI.POI_SERVICE_NOMINATIM) {
|
||||
marker = App.res.getDrawable(R.drawable.marker_poi_default);
|
||||
} else if (poi.serviceId == POI.POI_SERVICE_GEONAMES_WIKIPEDIA) {
|
||||
if (poi.rank < 90)
|
||||
marker = App.res.getDrawable(R.drawable.marker_poi_wikipedia_16);
|
||||
else
|
||||
marker = App.res.getDrawable(R.drawable.marker_poi_wikipedia_32);
|
||||
} else if (poi.serviceId == POI.POI_SERVICE_FLICKR) {
|
||||
marker = App.res.getDrawable(R.drawable.marker_poi_flickr);
|
||||
} else if (poi.serviceId == POI.POI_SERVICE_PICASA) {
|
||||
marker = App.res.getDrawable(R.drawable.marker_poi_picasa_24);
|
||||
poiMarker.setSubDescription(poi.category);
|
||||
}
|
||||
poiMarker.setMarker(marker);
|
||||
poiMarker.setMarkerHotspot(OverlayItem.HotspotPlace.CENTER);
|
||||
//thumbnail loading moved in POIInfoWindow.onOpen for better performances.
|
||||
poiMarker.setRelatedObject(poi);
|
||||
poiMarkers.addItem(poiMarker);
|
||||
}
|
||||
|
||||
Log.d(App.TAG, "SEND INTENT");
|
||||
|
||||
Intent intent = new Intent(tileMap.getApplicationContext(), POIActivity.class);
|
||||
intent.putParcelableArrayListExtra("POI", mPOIs);
|
||||
intent.putExtra("ID", poiMarkers.getBubbledItemId());
|
||||
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_SINGLE_TOP);
|
||||
tileMap.startActivityForResult(intent, TileMap.POIS_REQUEST);
|
||||
|
||||
}
|
||||
|
||||
App.map.redrawMap();
|
||||
}
|
||||
|
||||
void getPOIAsync(String tag) {
|
||||
poiMarkers.removeAllItems();
|
||||
new POITask().execute(tag);
|
||||
}
|
||||
}
|
||||
403
src/org/oscim/app/RouteSearch.java
Normal file
@ -0,0 +1,403 @@
|
||||
/*
|
||||
* Copyright 2012 osmdroid: M.Kergall
|
||||
* Copyright 2012 Hannes Janetzek
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it under the
|
||||
* terms of the GNU Lesser 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 Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.oscim.app;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.oscim.core.GeoPoint;
|
||||
import org.oscim.overlay.Overlay;
|
||||
import org.oscim.overlay.OverlayItem;
|
||||
import org.oscim.overlay.PathOverlay;
|
||||
import org.oscim.view.MapView;
|
||||
import org.osmdroid.location.GeocoderNominatim;
|
||||
import org.osmdroid.overlays.DefaultInfoWindow;
|
||||
import org.osmdroid.overlays.ExtendedOverlayItem;
|
||||
import org.osmdroid.overlays.ItemizedOverlayWithBubble;
|
||||
import org.osmdroid.overlays.MapEventsOverlay;
|
||||
import org.osmdroid.overlays.MapEventsReceiver;
|
||||
import org.osmdroid.routing.OSRMRoadManager;
|
||||
import org.osmdroid.routing.Road;
|
||||
import org.osmdroid.routing.RoadManager;
|
||||
import org.osmdroid.routing.RoadNode;
|
||||
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.location.Address;
|
||||
import android.os.AsyncTask;
|
||||
import android.util.Log;
|
||||
import android.view.MenuItem;
|
||||
import android.widget.Toast;
|
||||
|
||||
public class RouteSearch implements MapEventsReceiver {
|
||||
protected Road mRoad;
|
||||
protected PathOverlay mRoadOverlay;
|
||||
protected ItemizedOverlayWithBubble<ExtendedOverlayItem> mRoadNodeMarkers;
|
||||
protected GeoPoint startPoint, destinationPoint;
|
||||
protected ArrayList<GeoPoint> viaPoints;
|
||||
protected static int START_INDEX = -2, DEST_INDEX = -1;
|
||||
protected ExtendedOverlayItem markerStart, markerDestination;
|
||||
protected ItemizedOverlayWithBubble<ExtendedOverlayItem> itineraryMarkers;
|
||||
|
||||
private final TileMap tileMap;
|
||||
|
||||
RouteSearch(TileMap tileMap) {
|
||||
// keep context and mapview reference
|
||||
|
||||
this.tileMap = tileMap;
|
||||
|
||||
//To use MapEventsReceiver methods, we add a MapEventsOverlay:
|
||||
MapEventsOverlay overlay = new MapEventsOverlay(tileMap, this);
|
||||
tileMap.map.getOverlays().add(overlay);
|
||||
|
||||
// if (savedInstanceState == null) {
|
||||
// Location l = locationManager.getLastKnownLocation(LocationManager.GPS_PROVIDER);
|
||||
// if (l != null) {
|
||||
// startPoint = new GeoPoint(l.getLatitude(), l.getLongitude());
|
||||
// } else {
|
||||
// //we put a hard-coded start
|
||||
startPoint = new GeoPoint(53.067221, 8.78767);
|
||||
// }
|
||||
destinationPoint = null;
|
||||
viaPoints = new ArrayList<GeoPoint>();
|
||||
// mapController.setZoom(9);
|
||||
// mapController.setCenter(startPoint);
|
||||
// }
|
||||
|
||||
// Itinerary markers:
|
||||
final ArrayList<ExtendedOverlayItem> waypointsItems = new ArrayList<ExtendedOverlayItem>();
|
||||
itineraryMarkers = new ItemizedOverlayWithBubble<ExtendedOverlayItem>(tileMap.map, tileMap,
|
||||
waypointsItems, new ViaPointInfoWindow(R.layout.itinerary_bubble, tileMap.map));
|
||||
tileMap.map.getOverlays().add(itineraryMarkers);
|
||||
updateUIWithItineraryMarkers();
|
||||
|
||||
//Route and Directions
|
||||
final ArrayList<ExtendedOverlayItem> roadItems = new ArrayList<ExtendedOverlayItem>();
|
||||
mRoadNodeMarkers = new ItemizedOverlayWithBubble<ExtendedOverlayItem>(tileMap, roadItems,
|
||||
tileMap.map);
|
||||
tileMap.map.getOverlays().add(mRoadNodeMarkers);
|
||||
|
||||
// if (savedInstanceState != null) {
|
||||
// mRoad = savedInstanceState.getParcelable("road");
|
||||
// updateUIWithRoad(mRoad);
|
||||
// }
|
||||
|
||||
}
|
||||
|
||||
//------------- Geocoding and Reverse Geocoding
|
||||
|
||||
/**
|
||||
* Reverse Geocoding
|
||||
* @param p
|
||||
* ...
|
||||
* @return ...
|
||||
*/
|
||||
public String getAddress(GeoPoint p) {
|
||||
GeocoderNominatim geocoder = new GeocoderNominatim(tileMap);
|
||||
String theAddress;
|
||||
try {
|
||||
double dLatitude = p.getLatitude();
|
||||
double dLongitude = p.getLongitude();
|
||||
List<Address> addresses = geocoder.getFromLocation(dLatitude, dLongitude, 1);
|
||||
StringBuilder sb = new StringBuilder();
|
||||
if (addresses.size() > 0) {
|
||||
Address address = addresses.get(0);
|
||||
int n = address.getMaxAddressLineIndex();
|
||||
for (int i = 0; i <= n; i++) {
|
||||
if (i != 0)
|
||||
sb.append(", ");
|
||||
sb.append(address.getAddressLine(i));
|
||||
}
|
||||
theAddress = new String(sb.toString());
|
||||
} else {
|
||||
theAddress = null;
|
||||
}
|
||||
} catch (IOException e) {
|
||||
theAddress = null;
|
||||
}
|
||||
if (theAddress != null) {
|
||||
return theAddress;
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
//Async task to reverse-geocode the marker position in a separate thread:
|
||||
class GeocodingTask extends AsyncTask<Object, Void, String> {
|
||||
ExtendedOverlayItem marker;
|
||||
|
||||
@Override
|
||||
protected String doInBackground(Object... params) {
|
||||
marker = (ExtendedOverlayItem) params[0];
|
||||
return getAddress(marker.getPoint());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(String result) {
|
||||
marker.setDescription(result);
|
||||
//itineraryMarkers.showBubbleOnItem(???, map); //open bubble on the item
|
||||
}
|
||||
}
|
||||
|
||||
//------------ Itinerary markers
|
||||
|
||||
/* add (or replace) an item in markerOverlays. p position. */
|
||||
public ExtendedOverlayItem putMarkerItem(ExtendedOverlayItem item, GeoPoint p, int index,
|
||||
int titleResId, int markerResId, int iconResId) {
|
||||
|
||||
if (item != null) {
|
||||
itineraryMarkers.removeItem(item);
|
||||
}
|
||||
|
||||
Drawable marker = App.res.getDrawable(markerResId);
|
||||
String title = App.res.getString(titleResId);
|
||||
|
||||
ExtendedOverlayItem overlayItem = new ExtendedOverlayItem(title, "", p);
|
||||
overlayItem.setMarkerHotspot(OverlayItem.HotspotPlace.BOTTOM_CENTER);
|
||||
overlayItem.setMarker(marker);
|
||||
|
||||
if (iconResId != -1)
|
||||
overlayItem.setImage(App.res.getDrawable(iconResId));
|
||||
overlayItem.setRelatedObject(Integer.valueOf(index));
|
||||
|
||||
itineraryMarkers.addItem(overlayItem);
|
||||
|
||||
tileMap.map.redrawMap();
|
||||
//map.invalidate();
|
||||
|
||||
//Start geocoding task to update the description of the marker with its address:
|
||||
new GeocodingTask().execute(overlayItem);
|
||||
return overlayItem;
|
||||
}
|
||||
|
||||
public void addViaPoint(GeoPoint p) {
|
||||
viaPoints.add(p);
|
||||
putMarkerItem(null, p, viaPoints.size() - 1,
|
||||
R.string.viapoint, R.drawable.marker_via, -1);
|
||||
}
|
||||
|
||||
public void removePoint(int index) {
|
||||
if (index == START_INDEX)
|
||||
startPoint = null;
|
||||
else if (index == DEST_INDEX)
|
||||
destinationPoint = null;
|
||||
else
|
||||
viaPoints.remove(index);
|
||||
getRoadAsync();
|
||||
updateUIWithItineraryMarkers();
|
||||
}
|
||||
|
||||
public void updateUIWithItineraryMarkers() {
|
||||
itineraryMarkers.removeAllItems();
|
||||
//Start marker:
|
||||
if (startPoint != null) {
|
||||
markerStart = putMarkerItem(null, startPoint, START_INDEX,
|
||||
R.string.departure, R.drawable.marker_departure, -1);
|
||||
}
|
||||
//Via-points markers if any:
|
||||
for (int index = 0; index < viaPoints.size(); index++) {
|
||||
putMarkerItem(null, viaPoints.get(index), index,
|
||||
R.string.viapoint, R.drawable.marker_via, -1);
|
||||
}
|
||||
//Destination marker if any:
|
||||
if (destinationPoint != null) {
|
||||
markerDestination = putMarkerItem(null, destinationPoint, DEST_INDEX,
|
||||
R.string.destination,
|
||||
R.drawable.marker_destination, -1);
|
||||
}
|
||||
}
|
||||
|
||||
//------------ Route and Directions
|
||||
|
||||
private void putRoadNodes(Road road) {
|
||||
mRoadNodeMarkers.removeAllItems();
|
||||
Drawable marker = App.res.getDrawable(R.drawable.marker_node);
|
||||
int n = road.nodes.size();
|
||||
// TypedArray iconIds = App.res.obtainTypedArray(R.array.direction_icons);
|
||||
for (int i = 0; i < n; i++) {
|
||||
RoadNode node = road.nodes.get(i);
|
||||
String instructions = (node.instructions == null ? "" : node.instructions);
|
||||
ExtendedOverlayItem nodeMarker = new ExtendedOverlayItem(
|
||||
"Step " + (i + 1), instructions, node.location);
|
||||
|
||||
nodeMarker.setSubDescription(road.getLengthDurationText(node.length, node.duration));
|
||||
nodeMarker.setMarkerHotspot(OverlayItem.HotspotPlace.CENTER);
|
||||
nodeMarker.setMarker(marker);
|
||||
// int iconId = iconIds.getResourceId(node.mManeuverType, R.drawable.ic_empty);
|
||||
// if (iconId != R.drawable.ic_empty) {
|
||||
// Drawable icon = App.res.getDrawable(iconId);
|
||||
// nodeMarker.setImage(icon);
|
||||
// }
|
||||
mRoadNodeMarkers.addItem(nodeMarker);
|
||||
}
|
||||
}
|
||||
|
||||
void updateUIWithRoad(Road road) {
|
||||
mRoadNodeMarkers.removeAllItems();
|
||||
List<Overlay> mapOverlays = tileMap.map.getOverlays();
|
||||
if (mRoadOverlay != null) {
|
||||
mapOverlays.remove(mRoadOverlay);
|
||||
}
|
||||
if (road == null)
|
||||
return;
|
||||
if (road.status == Road.STATUS_DEFAULT)
|
||||
Toast.makeText(tileMap, "We have a problem to get the route",
|
||||
Toast.LENGTH_SHORT).show();
|
||||
mRoadOverlay = RoadManager.buildRoadOverlay(tileMap.map, road, tileMap);
|
||||
Overlay removedOverlay = mapOverlays.set(1, mRoadOverlay);
|
||||
//we set the road overlay at the "bottom", just above the MapEventsOverlay,
|
||||
//to avoid covering the other overlays.
|
||||
mapOverlays.add(removedOverlay);
|
||||
putRoadNodes(road);
|
||||
|
||||
tileMap.map.redrawMap();
|
||||
// map.invalidate();
|
||||
|
||||
//Set route info in the text view:
|
||||
|
||||
// ((TextView) findViewById(R.id.routeInfo)).setText(road.getLengthDurationText(-1));
|
||||
}
|
||||
|
||||
/**
|
||||
* Async task to get the road in a separate thread.
|
||||
*/
|
||||
class UpdateRoadTask extends AsyncTask<WayPoints, Void, Road> {
|
||||
@Override
|
||||
protected Road doInBackground(WayPoints... wp) {
|
||||
ArrayList<GeoPoint> waypoints = wp[0].waypoints;
|
||||
//RoadManager roadManager = new GoogleRoadManager();
|
||||
RoadManager roadManager = new OSRMRoadManager();
|
||||
roadManager.addRequestOption("");
|
||||
/* RoadManager roadManager = new MapQuestRoadManager(); Locale
|
||||
* locale = Locale.getDefault();
|
||||
* roadManager.addRequestOption("locale="+locale.getLanguage
|
||||
* ()+"_"+locale.getCountry()); */
|
||||
return roadManager.getRoad(waypoints);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(Road result) {
|
||||
mRoad = result;
|
||||
updateUIWithRoad(result);
|
||||
|
||||
/// ??? getPOIAsync(poiTagText.getText().toString());
|
||||
}
|
||||
}
|
||||
|
||||
// Just to make JAVA shut up!
|
||||
class WayPoints {
|
||||
public ArrayList<GeoPoint> waypoints;
|
||||
}
|
||||
|
||||
public void getRoadAsync() {
|
||||
mRoad = null;
|
||||
if (startPoint == null || destinationPoint == null) {
|
||||
updateUIWithRoad(mRoad);
|
||||
return;
|
||||
}
|
||||
ArrayList<GeoPoint> waypoints = new ArrayList<GeoPoint>(2);
|
||||
waypoints.add(startPoint);
|
||||
//add intermediate via points:
|
||||
for (GeoPoint p : viaPoints) {
|
||||
waypoints.add(p);
|
||||
}
|
||||
waypoints.add(destinationPoint);
|
||||
WayPoints wp = new WayPoints();
|
||||
wp.waypoints = waypoints;
|
||||
new UpdateRoadTask().execute(wp);
|
||||
}
|
||||
|
||||
//------------ MapEventsReceiver implementation
|
||||
|
||||
GeoPoint tempClickedGeoPoint; //any other way to pass the position to the menu ???
|
||||
|
||||
@Override
|
||||
public boolean longPressHelper(GeoPoint p) {
|
||||
Log.d(TileMap.TAG, ">>> got long press event " + p);
|
||||
tempClickedGeoPoint = p;
|
||||
//new GeoPoint((GeoPoint) p);
|
||||
//menu is hooked on the "Search" button
|
||||
// Button searchButton = (Button) findViewById(R.id.buttonSearch);
|
||||
// tileMap.openContextMenu(searchButton);
|
||||
|
||||
tileMap.openContextMenu(tileMap.map);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean singleTapUpHelper(GeoPoint p) {
|
||||
Log.d(App.TAG, "single tap up");
|
||||
|
||||
mRoadNodeMarkers.hideBubble();
|
||||
itineraryMarkers.hideBubble();
|
||||
return false;
|
||||
}
|
||||
|
||||
// ----------- context menu
|
||||
boolean onContextItemSelected(MenuItem item) {
|
||||
switch (item.getItemId()) {
|
||||
case R.id.menu_departure:
|
||||
startPoint = tempClickedGeoPoint;
|
||||
//new GeoPoint((GeoPoint) tempClickedGeoPoint);
|
||||
markerStart = putMarkerItem(markerStart, startPoint, START_INDEX,
|
||||
R.string.departure, R.drawable.marker_departure, -1);
|
||||
getRoadAsync();
|
||||
return true;
|
||||
case R.id.menu_destination:
|
||||
destinationPoint = tempClickedGeoPoint;
|
||||
//new GeoPoint((GeoPoint) tempClickedGeoPoint);
|
||||
markerDestination = putMarkerItem(markerDestination, destinationPoint, DEST_INDEX,
|
||||
R.string.destination,
|
||||
R.drawable.marker_destination, -1);
|
||||
getRoadAsync();
|
||||
return true;
|
||||
case R.id.menu_viapoint:
|
||||
GeoPoint viaPoint = tempClickedGeoPoint; //new GeoPoint((GeoPoint) tempClickedGeoPoint);
|
||||
addViaPoint(viaPoint);
|
||||
getRoadAsync();
|
||||
return true;
|
||||
default:
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
class ViaPointInfoWindow extends DefaultInfoWindow {
|
||||
|
||||
int mSelectedPoint;
|
||||
|
||||
public ViaPointInfoWindow(int layoutResId, MapView mapView) {
|
||||
super(layoutResId, mapView);
|
||||
|
||||
// Button btnDelete = (Button)(mView.findViewById(R.id.bubble_delete));
|
||||
// btnDelete.setOnClickListener(new View.OnClickListener() {
|
||||
// public void onClick(View view) {
|
||||
// //Call the removePoint method on MapActivity.
|
||||
// //TODO: find a cleaner way to do that!
|
||||
// MapActivity mapActivity = (MapActivity)view.getContext();
|
||||
// mapActivity.removePoint(mSelectedPoint);
|
||||
// close();
|
||||
// }
|
||||
// });
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onOpen(ExtendedOverlayItem item) {
|
||||
mSelectedPoint = ((Integer) item.getRelatedObject()).intValue();
|
||||
super.onOpen(item);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@ -1,3 +1,19 @@
|
||||
/* Copyright 2010, 2011, 2012 mapsforge.org
|
||||
* Copyright 2012 Hannes Janetzek
|
||||
* Copyright 2012 osmdroidbonuspack: M.Kergall
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it under the
|
||||
* terms of the GNU Lesser 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 Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.oscim.app;
|
||||
|
||||
import java.io.FileFilter;
|
||||
@ -7,8 +23,6 @@ import org.oscim.app.filefilter.FilterByFileExtension;
|
||||
import org.oscim.app.filefilter.ValidRenderTheme;
|
||||
import org.oscim.app.filepicker.FilePicker;
|
||||
import org.oscim.app.preferences.EditPreferences;
|
||||
import org.oscim.core.GeoPoint;
|
||||
import org.oscim.core.MapPosition;
|
||||
import org.oscim.database.MapDatabases;
|
||||
import org.oscim.theme.InternalRenderTheme;
|
||||
import org.oscim.utils.AndroidUtils;
|
||||
@ -19,7 +33,6 @@ import org.oscim.view.MapView;
|
||||
import android.app.AlertDialog;
|
||||
import android.app.Dialog;
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.content.pm.ActivityInfo;
|
||||
@ -29,57 +42,96 @@ import android.os.PowerManager;
|
||||
import android.os.PowerManager.WakeLock;
|
||||
import android.preference.PreferenceManager;
|
||||
import android.util.Log;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.ContextMenu;
|
||||
import android.view.ContextMenu.ContextMenuInfo;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuInflater;
|
||||
import android.view.MenuItem;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
import android.view.WindowManager;
|
||||
import android.widget.EditText;
|
||||
import android.widget.SeekBar;
|
||||
import android.widget.SpinnerAdapter;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
/**
|
||||
* A map application which uses the features from the mapsforge map library. The
|
||||
* map can be centered to the current location. A simple file browser for
|
||||
* selecting the map file is also included. Some preferences can be adjusted via
|
||||
* the {@link EditPreferences} activity.
|
||||
*/
|
||||
public class TileMap extends MapActivity {
|
||||
// implements ActionBar.OnNavigationListener {
|
||||
static final String TAG = TileMap.class.getSimpleName();
|
||||
|
||||
MapView map;
|
||||
|
||||
// private static final String BUNDLE_CENTER_AT_FIRST_FIX =
|
||||
// "centerAtFirstFix";
|
||||
|
||||
private static final String BUNDLE_SHOW_MY_LOCATION = "showMyLocation";
|
||||
private static final String BUNDLE_SNAP_TO_LOCATION = "snapToLocation";
|
||||
private static final int DIALOG_ENTER_COORDINATES = 0;
|
||||
private static final int DIALOG_INFO_MAP_FILE = 1;
|
||||
// private static final int DIALOG_INFO_MAP_FILE = 1;
|
||||
private static final int DIALOG_LOCATION_PROVIDER_DISABLED = 2;
|
||||
|
||||
// private static final FileFilter FILE_FILTER_EXTENSION_MAP =
|
||||
// new FilterByFileExtension(".map");
|
||||
private static final FileFilter FILE_FILTER_EXTENSION_XML =
|
||||
new FilterByFileExtension(".xml");
|
||||
// private static final int SELECT_MAP_FILE = 0;
|
||||
|
||||
// Intents
|
||||
private static final int SELECT_RENDER_THEME_FILE = 1;
|
||||
protected static final int POIS_REQUEST = 2;
|
||||
|
||||
LocationHandler mLocation;
|
||||
|
||||
private MapDatabases mMapDatabase;
|
||||
|
||||
private WakeLock mWakeLock;
|
||||
MapView mMapView;
|
||||
private Menu mMenu = null;
|
||||
|
||||
SpinnerAdapter mSpinnerAdapter;
|
||||
POISearch mPoiSearch;
|
||||
RouteSearch mRouteSearch;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
// set up the layout views
|
||||
setContentView(R.layout.activity_tilemap);
|
||||
|
||||
map = (MapView) findViewById(R.id.mapView);
|
||||
|
||||
App.map = map;
|
||||
|
||||
// configure the MapView and activate the zoomLevel buttons
|
||||
map.setClickable(true);
|
||||
// map.setBuiltInZoomControls(true);
|
||||
map.setFocusable(true);
|
||||
|
||||
mLocation = new LocationHandler(this);
|
||||
|
||||
// get the pointers to different system services
|
||||
PowerManager powerManager = (PowerManager) getSystemService(Context.POWER_SERVICE);
|
||||
|
||||
mWakeLock = powerManager
|
||||
.newWakeLock(PowerManager.SCREEN_DIM_WAKE_LOCK, "AMV");
|
||||
|
||||
if (savedInstanceState != null) {
|
||||
if (savedInstanceState.getBoolean(BUNDLE_SHOW_MY_LOCATION)) {
|
||||
|
||||
// enableShowMyLocation(savedInstanceState
|
||||
// .getBoolean(BUNDLE_CENTER_AT_FIRST_FIX));
|
||||
|
||||
if (savedInstanceState.getBoolean(BUNDLE_SNAP_TO_LOCATION)) {
|
||||
mLocation.enableSnapToLocation(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
App.poiSearch = mPoiSearch = new POISearch(this);
|
||||
|
||||
registerForContextMenu(map);
|
||||
mRouteSearch = new RouteSearch(this);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCreateOptionsMenu(Menu menu) {
|
||||
// if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB)
|
||||
// getMenuInflater().inflate(R.menu.options_menu, menu);
|
||||
// else
|
||||
getMenuInflater().inflate(R.menu.options_menu_pre_honeycomb, menu);
|
||||
|
||||
getMenuInflater().inflate(R.menu.options_menu, menu);
|
||||
mMenu = menu;
|
||||
|
||||
return true;
|
||||
@ -97,22 +149,22 @@ public class TileMap extends MapActivity {
|
||||
return true;
|
||||
|
||||
case R.id.menu_rotation_enable:
|
||||
mMapView.enableRotation(true);
|
||||
map.enableRotation(true);
|
||||
toggleMenuRotation();
|
||||
return true;
|
||||
|
||||
case R.id.menu_rotation_disable:
|
||||
mMapView.enableRotation(false);
|
||||
map.enableRotation(false);
|
||||
toggleMenuRotation();
|
||||
return true;
|
||||
|
||||
case R.id.menu_compass_enable:
|
||||
mMapView.enableCompass(true);
|
||||
map.enableCompass(true);
|
||||
toggleMenuRotation();
|
||||
return true;
|
||||
|
||||
case R.id.menu_compass_disable:
|
||||
mMapView.enableCompass(false);
|
||||
map.enableCompass(false);
|
||||
toggleMenuRotation();
|
||||
return true;
|
||||
|
||||
@ -130,10 +182,6 @@ public class TileMap extends MapActivity {
|
||||
mLocation.disableShowMyLocation());
|
||||
return true;
|
||||
|
||||
// case R.id.menu_position_last_known:
|
||||
// mLocation.gotoLastKnownPosition();
|
||||
// return true;
|
||||
|
||||
case R.id.menu_position_enter_coordinates:
|
||||
showDialog(DIALOG_ENTER_COORDINATES);
|
||||
return true;
|
||||
@ -145,15 +193,12 @@ public class TileMap extends MapActivity {
|
||||
case R.id.menu_render_theme:
|
||||
return true;
|
||||
|
||||
case R.id.menu_options:
|
||||
return true;
|
||||
|
||||
case R.id.menu_render_theme_osmarender:
|
||||
mMapView.setRenderTheme(InternalRenderTheme.OSMARENDER);
|
||||
map.setRenderTheme(InternalRenderTheme.OSMARENDER);
|
||||
return true;
|
||||
|
||||
case R.id.menu_render_theme_tronrender:
|
||||
mMapView.setRenderTheme(InternalRenderTheme.TRONRENDER);
|
||||
map.setRenderTheme(InternalRenderTheme.TRONRENDER);
|
||||
return true;
|
||||
|
||||
case R.id.menu_render_theme_select_file:
|
||||
@ -162,15 +207,29 @@ public class TileMap extends MapActivity {
|
||||
|
||||
// case R.id.menu_position_map_center:
|
||||
// // disable GPS follow mode if it is enabled
|
||||
// mLocation.disableSnapToLocation(true);
|
||||
// location.disableSnapToLocation(true);
|
||||
//
|
||||
// mMapView.setCenter(mMapView.getMapDatabase()
|
||||
// map.setCenter(map.getMapDatabase()
|
||||
// .getMapInfo().mapCenter);
|
||||
// return true;
|
||||
// case R.id.menu_mapfile:
|
||||
// startMapFilePicker();
|
||||
// return true;
|
||||
|
||||
case R.id.menu_pois:
|
||||
mPoiSearch.getPOIAsync("bar");
|
||||
// Intent myIntent = new Intent(this, POIActivity.class);
|
||||
// myIntent.putParcelableArrayListExtra("POI", mPOIs);
|
||||
// // myIntent.putExtra("ID", poiMarkers.getBubbledItemId());
|
||||
// startActivityForResult(myIntent, POIS_REQUEST);
|
||||
return true;
|
||||
|
||||
case R.id.menu_poi_list:
|
||||
Intent myIntent = new Intent(this, POIActivity.class);
|
||||
myIntent.putParcelableArrayListExtra("POI", mPoiSearch.mPOIs);
|
||||
myIntent.putExtra("ID", mPoiSearch.poiMarkers.getBubbledItemId());
|
||||
startActivityForResult(myIntent, POIS_REQUEST);
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
@ -181,12 +240,12 @@ public class TileMap extends MapActivity {
|
||||
toggleMenuItem(mMenu,
|
||||
R.id.menu_rotation_enable,
|
||||
R.id.menu_rotation_disable,
|
||||
!mMapView.enableRotation);
|
||||
!map.enableRotation);
|
||||
|
||||
toggleMenuItem(mMenu,
|
||||
R.id.menu_compass_enable,
|
||||
R.id.menu_compass_disable,
|
||||
!mMapView.enableCompass);
|
||||
!map.enableCompass);
|
||||
}
|
||||
|
||||
private static void toggleMenuItem(Menu menu, int id, int id2, boolean enable) {
|
||||
@ -210,7 +269,7 @@ public class TileMap extends MapActivity {
|
||||
!mLocation.isShowMyLocationEnabled());
|
||||
|
||||
if (mMapDatabase == MapDatabases.MAP_READER) {
|
||||
menu.findItem(R.id.menu_mapfile).setVisible(true);
|
||||
//menu.findItem(R.id.menu_mapfile).setVisible(true);
|
||||
menu.findItem(R.id.menu_position_map_center).setVisible(true);
|
||||
}
|
||||
// else {
|
||||
@ -226,7 +285,7 @@ public class TileMap extends MapActivity {
|
||||
@Override
|
||||
public boolean onTrackballEvent(MotionEvent event) {
|
||||
// forward the event to the MapView
|
||||
return mMapView.onTrackballEvent(event);
|
||||
return map.onTrackballEvent(event);
|
||||
}
|
||||
|
||||
// private void startMapFilePicker() {
|
||||
@ -245,14 +304,40 @@ public class TileMap extends MapActivity {
|
||||
|
||||
@Override
|
||||
protected void onActivityResult(int requestCode, int resultCode, Intent intent) {
|
||||
switch (requestCode) {
|
||||
case POIS_REQUEST:
|
||||
Log.d(TAG, "result: POIS_REQUEST");
|
||||
if (resultCode == RESULT_OK) {
|
||||
int id = intent.getIntExtra("ID", 0);
|
||||
Log.d(TAG, "result: POIS_REQUEST: " + id);
|
||||
// map.getController().setCenter(mPOIs.get(id).mLocation);
|
||||
mPoiSearch.poiMarkers.showBubbleOnItem(id, map);
|
||||
map.getMapViewPosition().animateTo(mPoiSearch.mPOIs.get(id).location);
|
||||
}
|
||||
break;
|
||||
case SELECT_RENDER_THEME_FILE:
|
||||
if (resultCode == RESULT_OK && intent != null
|
||||
&& intent.getStringExtra(FilePicker.SELECTED_FILE) != null) {
|
||||
try {
|
||||
map.setRenderTheme(intent
|
||||
.getStringExtra(FilePicker.SELECTED_FILE));
|
||||
} catch (FileNotFoundException e) {
|
||||
showToastOnUiThread(e.getLocalizedMessage());
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
// if (requestCode == SELECT_MAP_FILE) {
|
||||
// if (resultCode == RESULT_OK) {
|
||||
//
|
||||
// mLocation.disableSnapToLocation(true);
|
||||
// location.disableSnapToLocation(true);
|
||||
//
|
||||
// if (intent != null) {
|
||||
// if (intent.getStringExtra(FilePicker.SELECTED_FILE) != null) {
|
||||
// mMapView.setMapFile(intent
|
||||
// map.setMapFile(intent
|
||||
// .getStringExtra(FilePicker.SELECTED_FILE));
|
||||
// }
|
||||
// }
|
||||
@ -260,122 +345,37 @@ public class TileMap extends MapActivity {
|
||||
// startActivity(new Intent(this, EditPreferences.class));
|
||||
// }
|
||||
// } else
|
||||
if (requestCode == SELECT_RENDER_THEME_FILE && resultCode == RESULT_OK
|
||||
&& intent != null
|
||||
&& intent.getStringExtra(FilePicker.SELECTED_FILE) != null) {
|
||||
try {
|
||||
mMapView.setRenderTheme(intent
|
||||
.getStringExtra(FilePicker.SELECTED_FILE));
|
||||
} catch (FileNotFoundException e) {
|
||||
showToastOnUiThread(e.getLocalizedMessage());
|
||||
}
|
||||
}
|
||||
// if (requestCode == SELECT_RENDER_THEME_FILE && resultCode == RESULT_OK
|
||||
// && intent != null
|
||||
// && intent.getStringExtra(FilePicker.SELECTED_FILE) != null) {
|
||||
// try {
|
||||
// map.setRenderTheme(intent
|
||||
// .getStringExtra(FilePicker.SELECTED_FILE));
|
||||
// } catch (FileNotFoundException e) {
|
||||
// showToastOnUiThread(e.getLocalizedMessage());
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
static boolean isPreHoneyComb() {
|
||||
return Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB;
|
||||
}
|
||||
|
||||
// @TargetApi(11)
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
// if (!isPreHoneyComb()) {
|
||||
// mSpinnerAdapter = ArrayAdapter.createFromResource(this,
|
||||
// R.array.view_sections,
|
||||
// android.R.layout.simple_spinner_dropdown_item);
|
||||
// ActionBar actionBar = getActionBar();
|
||||
// actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_LIST);
|
||||
//
|
||||
// // actionBar.setListNavigationCallbacks(mSpinnerAdapter, this);
|
||||
// actionBar.setDisplayShowTitleEnabled(false);
|
||||
// }
|
||||
|
||||
// set up the layout views
|
||||
setContentView(R.layout.activity_tilemap);
|
||||
|
||||
mMapView = (MapView) findViewById(R.id.mapView);
|
||||
|
||||
// configure the MapView and activate the zoomLevel buttons
|
||||
mMapView.setClickable(true);
|
||||
// mMapView.setBuiltInZoomControls(true);
|
||||
mMapView.setFocusable(true);
|
||||
|
||||
mLocation = new LocationHandler(this);
|
||||
|
||||
// get the pointers to different system services
|
||||
PowerManager powerManager = (PowerManager) getSystemService(Context.POWER_SERVICE);
|
||||
|
||||
mWakeLock = powerManager
|
||||
.newWakeLock(PowerManager.SCREEN_DIM_WAKE_LOCK, "AMV");
|
||||
|
||||
if (savedInstanceState != null) {
|
||||
if (savedInstanceState.getBoolean(BUNDLE_SHOW_MY_LOCATION)) {
|
||||
|
||||
// enableShowMyLocation(savedInstanceState
|
||||
// .getBoolean(BUNDLE_CENTER_AT_FIRST_FIX));
|
||||
|
||||
if (savedInstanceState.getBoolean(BUNDLE_SNAP_TO_LOCATION)) {
|
||||
mLocation.enableSnapToLocation(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Dialog onCreateDialog(int id) {
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(this);
|
||||
if (id == DIALOG_ENTER_COORDINATES) {
|
||||
builder.setIcon(android.R.drawable.ic_menu_mylocation);
|
||||
builder.setTitle(R.string.menu_position_enter_coordinates);
|
||||
LayoutInflater factory = LayoutInflater.from(this);
|
||||
final View view = factory.inflate(R.layout.dialog_enter_coordinates, null);
|
||||
builder.setView(view);
|
||||
if (mLocationDialog == null)
|
||||
mLocationDialog = new LocationDialog();
|
||||
|
||||
builder.setPositiveButton(R.string.go_to_position,
|
||||
new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
// disable GPS follow mode if it is enabled
|
||||
mLocation.disableSnapToLocation(true);
|
||||
return mLocationDialog.createDialog(this);
|
||||
|
||||
// set the map center and zoom level
|
||||
EditText latitudeView = (EditText) view
|
||||
.findViewById(R.id.latitude);
|
||||
EditText longitudeView = (EditText) view
|
||||
.findViewById(R.id.longitude);
|
||||
double latitude = Double.parseDouble(latitudeView.getText()
|
||||
.toString());
|
||||
double longitude = Double.parseDouble(longitudeView.getText()
|
||||
.toString());
|
||||
|
||||
SeekBar zoomLevelView = (SeekBar) view
|
||||
.findViewById(R.id.zoomLevel);
|
||||
|
||||
byte zoom = (byte) (zoomLevelView.getProgress());
|
||||
|
||||
MapPosition mapPosition = new MapPosition(latitude,
|
||||
longitude, zoom, 1, 0);
|
||||
|
||||
TileMap.this.mMapView.setMapCenter(mapPosition);
|
||||
}
|
||||
});
|
||||
builder.setNegativeButton(R.string.cancel, null);
|
||||
return builder.create();
|
||||
} else if (id == DIALOG_LOCATION_PROVIDER_DISABLED) {
|
||||
builder.setIcon(android.R.drawable.ic_menu_info_details);
|
||||
builder.setTitle(R.string.error);
|
||||
builder.setMessage(R.string.no_location_provider_available);
|
||||
builder.setPositiveButton(R.string.ok, null);
|
||||
return builder.create();
|
||||
} else if (id == DIALOG_INFO_MAP_FILE) {
|
||||
builder.setIcon(android.R.drawable.ic_menu_info_details);
|
||||
builder.setTitle(R.string.menu_info_map_file);
|
||||
LayoutInflater factory = LayoutInflater.from(this);
|
||||
builder.setView(factory.inflate(R.layout.dialog_info_map_file, null));
|
||||
builder.setPositiveButton(R.string.ok, null);
|
||||
return builder.create();
|
||||
} else {
|
||||
// no dialog will be created
|
||||
return null;
|
||||
@ -390,6 +390,7 @@ public class TileMap extends MapActivity {
|
||||
|
||||
@Override
|
||||
protected void onPause() {
|
||||
Log.d(TAG, "onPause");
|
||||
super.onPause();
|
||||
// release the wake lock if necessary
|
||||
if (mWakeLock.isHeld()) {
|
||||
@ -397,91 +398,14 @@ public class TileMap extends MapActivity {
|
||||
}
|
||||
}
|
||||
|
||||
LocationDialog mLocationDialog;
|
||||
|
||||
@Override
|
||||
protected void onPrepareDialog(int id, final Dialog dialog) {
|
||||
if (id == DIALOG_ENTER_COORDINATES) {
|
||||
EditText editText = (EditText) dialog.findViewById(R.id.latitude);
|
||||
GeoPoint mapCenter = mMapView.getMapPosition().getMapCenter();
|
||||
editText.setText(Double.toString(mapCenter.getLatitude()));
|
||||
|
||||
editText = (EditText) dialog.findViewById(R.id.longitude);
|
||||
editText.setText(Double.toString(mapCenter.getLongitude()));
|
||||
mLocationDialog.prepareDialog(map, dialog);
|
||||
|
||||
SeekBar zoomlevel = (SeekBar) dialog.findViewById(R.id.zoomLevel);
|
||||
zoomlevel.setMax(20); // FIXME
|
||||
// mMapView.getMapGenerator().getZoomLevelMax());
|
||||
zoomlevel.setProgress(mMapView.getMapPosition().getZoomLevel());
|
||||
|
||||
final TextView textView = (TextView) dialog.findViewById(R.id.zoomlevelValue);
|
||||
textView.setText(String.valueOf(zoomlevel.getProgress()));
|
||||
zoomlevel.setOnSeekBarChangeListener(new SeekBarChangeListener(textView));
|
||||
// } else if (id == DIALOG_INFO_MAP_FILE) {
|
||||
// MapInfo mapInfo = mMapView.getMapDatabase().getMapInfo();
|
||||
//
|
||||
// TextView textView = (TextView)
|
||||
// dialog.findViewById(R.id.infoMapFileViewName);
|
||||
// textView.setText(mMapView.getMapFile());
|
||||
//
|
||||
// textView = (TextView)
|
||||
// dialog.findViewById(R.id.infoMapFileViewSize);
|
||||
// textView.setText(FileUtils.formatFileSize(mapInfo.fileSize,
|
||||
// getResources()));
|
||||
//
|
||||
// textView = (TextView)
|
||||
// dialog.findViewById(R.id.infoMapFileViewVersion);
|
||||
// textView.setText(String.valueOf(mapInfo.fileVersion));
|
||||
//
|
||||
// // textView = (TextView)
|
||||
// dialog.findViewById(R.id.infoMapFileViewDebug);
|
||||
// // if (mapFileInfo.debugFile) {
|
||||
// // textView.setText(R.string.info_map_file_debug_yes);
|
||||
// // } else {
|
||||
// // textView.setText(R.string.info_map_file_debug_no);
|
||||
// // }
|
||||
//
|
||||
// textView = (TextView)
|
||||
// dialog.findViewById(R.id.infoMapFileViewDate);
|
||||
// Date date = new Date(mapInfo.mapDate);
|
||||
// textView.setText(DateFormat.getDateTimeInstance().format(date));
|
||||
//
|
||||
// textView = (TextView)
|
||||
// dialog.findViewById(R.id.infoMapFileViewArea);
|
||||
// BoundingBox boundingBox = mapInfo.boundingBox;
|
||||
// textView.setText(boundingBox.getMinLatitude() + ", "
|
||||
// + boundingBox.getMinLongitude() + " - \n"
|
||||
// + boundingBox.getMaxLatitude() + ", " +
|
||||
// boundingBox.getMaxLongitude());
|
||||
//
|
||||
// textView = (TextView)
|
||||
// dialog.findViewById(R.id.infoMapFileViewStartPosition);
|
||||
// GeoPoint startPosition = mapInfo.startPosition;
|
||||
// if (startPosition == null) {
|
||||
// textView.setText(null);
|
||||
// } else {
|
||||
// textView.setText(startPosition.getLatitude() + ", "
|
||||
// + startPosition.getLongitude());
|
||||
// }
|
||||
//
|
||||
// textView = (TextView)
|
||||
// dialog.findViewById(R.id.infoMapFileViewStartZoomLevel);
|
||||
// Byte startZoomLevel = mapInfo.startZoomLevel;
|
||||
// if (startZoomLevel == null) {
|
||||
// textView.setText(null);
|
||||
// } else {
|
||||
// textView.setText(startZoomLevel.toString());
|
||||
// }
|
||||
//
|
||||
// textView = (TextView) dialog
|
||||
// .findViewById(R.id.infoMapFileViewLanguagePreference);
|
||||
// textView.setText(mapInfo.languagePreference);
|
||||
//
|
||||
// textView = (TextView)
|
||||
// dialog.findViewById(R.id.infoMapFileViewComment);
|
||||
// textView.setText(mapInfo.comment);
|
||||
//
|
||||
// textView = (TextView)
|
||||
// dialog.findViewById(R.id.infoMapFileViewCreatedBy);
|
||||
// textView.setText(mapInfo.createdBy);
|
||||
} else {
|
||||
super.onPrepareDialog(id, dialog);
|
||||
}
|
||||
@ -503,24 +427,6 @@ public class TileMap extends MapActivity {
|
||||
// scaleBarUnitDefault);
|
||||
// mapScaleBar.setImperialUnits(scaleBarUnit.equals("imperial"));
|
||||
|
||||
// if (preferences.contains("mapGenerator")) {
|
||||
// String name = preferences.getString("mapGenerator",
|
||||
// MapGeneratorInternal.SW_RENDERER.name());
|
||||
// MapGeneratorInternal mapGeneratorInternalNew;
|
||||
// try {
|
||||
// mapGeneratorInternalNew = MapGeneratorInternal.valueOf(name);
|
||||
// } catch (IllegalArgumentException e) {
|
||||
// mapGeneratorInternalNew = MapGeneratorInternal.SW_RENDERER;
|
||||
// }
|
||||
//
|
||||
// if (mapGeneratorInternalNew != mapGeneratorInternal) {
|
||||
// TileGenerator mapGenerator =
|
||||
// MapGeneratorFactory.createMapGenerator(mapGeneratorInternalNew);
|
||||
// mapView.setMapGenerator(mapGenerator);
|
||||
// mapGeneratorInternal = mapGeneratorInternalNew;
|
||||
// }
|
||||
// }
|
||||
|
||||
if (preferences.contains("mapDatabase")) {
|
||||
String name = preferences.getString("mapDatabase",
|
||||
MapDatabases.PBMAP_READER.name());
|
||||
@ -533,10 +439,10 @@ public class TileMap extends MapActivity {
|
||||
mapDatabaseNew = MapDatabases.PBMAP_READER;
|
||||
}
|
||||
|
||||
Log.d("VectorTileMap", "set map database " + mapDatabaseNew);
|
||||
|
||||
if (mapDatabaseNew != mMapDatabase) {
|
||||
mMapView.setMapDatabase(mapDatabaseNew);
|
||||
Log.d(TAG, "set map database " + mapDatabaseNew);
|
||||
|
||||
map.setMapDatabase(mapDatabaseNew);
|
||||
mMapDatabase = mapDatabaseNew;
|
||||
}
|
||||
}
|
||||
@ -544,10 +450,10 @@ public class TileMap extends MapActivity {
|
||||
// try {
|
||||
// String textScaleDefault =
|
||||
// getString(R.string.preferences_text_scale_default);
|
||||
// mMapView.setTextScale(Float.parseFloat(preferences.getString("textScale",
|
||||
// map.setTextScale(Float.parseFloat(preferences.getString("textScale",
|
||||
// textScaleDefault)));
|
||||
// } catch (NumberFormatException e) {
|
||||
// mMapView.setTextScale(1);
|
||||
// map.setTextScale(1);
|
||||
// }
|
||||
|
||||
if (preferences.getBoolean("fullscreen", false)) {
|
||||
@ -579,30 +485,29 @@ public class TileMap extends MapActivity {
|
||||
boolean drawUnmatchedWays =
|
||||
preferences.getBoolean("drawUnmatchedWays", false);
|
||||
|
||||
DebugSettings debugSettings = new DebugSettings(drawTileCoordinates,
|
||||
drawTileFrames, disablePolygons, drawUnmatchedWays);
|
||||
DebugSettings cur = map.getDebugSettings();
|
||||
if (cur.mDisablePolygons != disablePolygons
|
||||
|| cur.mDrawTileCoordinates != drawTileCoordinates
|
||||
|| cur.mDrawTileFrames != drawTileFrames
|
||||
|| cur.mDrawUnmatchted != drawUnmatchedWays) {
|
||||
Log.d(TAG, "set map debug settings");
|
||||
|
||||
mMapView.setDebugSettings(debugSettings);
|
||||
DebugSettings debugSettings = new DebugSettings(drawTileCoordinates,
|
||||
drawTileFrames, disablePolygons, drawUnmatchedWays);
|
||||
|
||||
map.setDebugSettings(debugSettings);
|
||||
}
|
||||
|
||||
// if (mMapDatabase == MapDatabases.MAP_READER) {
|
||||
// if (mMapView.getMapFile() == null)
|
||||
// if (map.getMapFile() == null)
|
||||
// startMapFilePicker();
|
||||
// } else {
|
||||
// mMapView.setMapFile(mMapView.getMapFile());
|
||||
// map.setMapFile(map.getMapFile());
|
||||
// }
|
||||
|
||||
// if (Build.VERSION.SDK_INT >= 11) {
|
||||
// VersionHelper.refreshActionBarMenu(this);
|
||||
// }
|
||||
map.redrawMap();
|
||||
}
|
||||
|
||||
// static class VersionHelper {
|
||||
// @TargetApi(11)
|
||||
// static void refreshActionBarMenu(Activity activity) {
|
||||
// activity.invalidateOptionsMenu();
|
||||
// }
|
||||
// }
|
||||
|
||||
@Override
|
||||
protected void onSaveInstanceState(Bundle outState) {
|
||||
super.onSaveInstanceState(outState);
|
||||
@ -616,7 +521,6 @@ public class TileMap extends MapActivity {
|
||||
/**
|
||||
* Uses the UI thread to display the given text message as toast
|
||||
* notification.
|
||||
*
|
||||
* @param text
|
||||
* the text message to display
|
||||
*/
|
||||
@ -636,33 +540,25 @@ public class TileMap extends MapActivity {
|
||||
}
|
||||
}
|
||||
|
||||
// @Override
|
||||
// public boolean onNavigationItemSelected(int arg0, long arg1) {
|
||||
// // TODO Auto-generated method stub
|
||||
// return false;
|
||||
// }
|
||||
//----------- Context Menu when clicking on the map
|
||||
@Override
|
||||
public void onCreateContextMenu(ContextMenu menu, View v,
|
||||
ContextMenuInfo menuInfo) {
|
||||
super.onCreateContextMenu(menu, v, menuInfo);
|
||||
Log.d(TAG, "create context menu");
|
||||
|
||||
class SeekBarChangeListener implements SeekBar.OnSeekBarChangeListener {
|
||||
private final TextView textView;
|
||||
MenuInflater inflater = getMenuInflater();
|
||||
inflater.inflate(R.menu.map_menu, menu);
|
||||
}
|
||||
|
||||
SeekBarChangeListener(TextView textView) {
|
||||
this.textView = textView;
|
||||
}
|
||||
@Override
|
||||
public boolean onContextItemSelected(MenuItem item) {
|
||||
Log.d(TAG, "context menu item selected " + item.getItemId());
|
||||
|
||||
@Override
|
||||
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
|
||||
this.textView.setText(String.valueOf(progress));
|
||||
}
|
||||
if (mRouteSearch.onContextItemSelected(item))
|
||||
return true;
|
||||
|
||||
@Override
|
||||
public void onStartTrackingTouch(SeekBar seekBar) {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStopTrackingTouch(SeekBar seekBar) {
|
||||
// do nothing
|
||||
}
|
||||
return super.onContextItemSelected(item);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -29,14 +29,16 @@ import android.widget.SeekBar.OnSeekBarChangeListener;
|
||||
import android.widget.TextView;
|
||||
|
||||
/**
|
||||
* This abstract class provides all code for a seek bar preference. Deriving classes only need to set the current and
|
||||
* maximum value of the seek bar. An optional text message above the seek bar is also supported as well as an optional
|
||||
* This abstract class provides all code for a seek bar preference. Deriving
|
||||
* classes only need to set the current and
|
||||
* maximum value of the seek bar. An optional text message above the seek bar is
|
||||
* also supported as well as an optional
|
||||
* current value message below the seek bar.
|
||||
*/
|
||||
abstract class SeekBarPreference extends DialogPreference implements OnSeekBarChangeListener {
|
||||
private TextView currentValueTextView;
|
||||
private Editor editor;
|
||||
private SeekBar preferenceSeekBar;
|
||||
private TextView mCurrentValueTextView;
|
||||
private Editor mEditor;
|
||||
private SeekBar mPreferenceSeekBar;
|
||||
|
||||
/**
|
||||
* How much the value should increase when the seek bar is moved.
|
||||
@ -65,7 +67,6 @@ abstract class SeekBarPreference extends DialogPreference implements OnSeekBarCh
|
||||
|
||||
/**
|
||||
* Create a new seek bar preference.
|
||||
*
|
||||
* @param context
|
||||
* the context of the seek bar preferences activity.
|
||||
* @param attrs
|
||||
@ -73,26 +74,26 @@ abstract class SeekBarPreference extends DialogPreference implements OnSeekBarCh
|
||||
*/
|
||||
SeekBarPreference(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
this.preferencesDefault = PreferenceManager.getDefaultSharedPreferences(context);
|
||||
preferencesDefault = PreferenceManager.getDefaultSharedPreferences(context);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
// check if the "OK" button was pressed and the seek bar value has changed
|
||||
if (which == DialogInterface.BUTTON_POSITIVE
|
||||
&& this.seekBarCurrentValue != this.preferenceSeekBar.getProgress()) {
|
||||
&& seekBarCurrentValue != mPreferenceSeekBar.getProgress()) {
|
||||
// get the value of the seek bar and save it in the preferences
|
||||
this.seekBarCurrentValue = this.preferenceSeekBar.getProgress();
|
||||
this.editor = this.preferencesDefault.edit();
|
||||
this.editor.putInt(this.getKey(), this.seekBarCurrentValue);
|
||||
this.editor.commit();
|
||||
seekBarCurrentValue = mPreferenceSeekBar.getProgress();
|
||||
mEditor = preferencesDefault.edit();
|
||||
mEditor.putInt(getKey(), seekBarCurrentValue);
|
||||
mEditor.commit();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
|
||||
if (this.currentValueTextView != null) {
|
||||
this.currentValueTextView.setText(getCurrentValueText(progress));
|
||||
if (mCurrentValueTextView != null) {
|
||||
mCurrentValueTextView.setText(getCurrentValueText(progress));
|
||||
}
|
||||
}
|
||||
|
||||
@ -114,38 +115,37 @@ abstract class SeekBarPreference extends DialogPreference implements OnSeekBarCh
|
||||
linearLayout.setPadding(20, 10, 20, 10);
|
||||
|
||||
// check if a text message should appear above the seek bar
|
||||
if (this.messageText != null) {
|
||||
if (messageText != null) {
|
||||
// create a text view for the text messageText
|
||||
TextView messageTextView = new TextView(getContext());
|
||||
messageTextView.setText(this.messageText);
|
||||
messageTextView.setText(messageText);
|
||||
messageTextView.setPadding(0, 0, 0, 20);
|
||||
// add the text message view to the layout
|
||||
linearLayout.addView(messageTextView);
|
||||
}
|
||||
|
||||
// create the seek bar and set the maximum and current value
|
||||
this.preferenceSeekBar = new SeekBar(getContext());
|
||||
this.preferenceSeekBar.setOnSeekBarChangeListener(this);
|
||||
this.preferenceSeekBar.setMax(this.max);
|
||||
this.preferenceSeekBar.setProgress(Math.min(this.seekBarCurrentValue, this.max));
|
||||
this.preferenceSeekBar.setKeyProgressIncrement(this.increment);
|
||||
this.preferenceSeekBar.setPadding(0, 0, 0, 10);
|
||||
mPreferenceSeekBar = new SeekBar(getContext());
|
||||
mPreferenceSeekBar.setOnSeekBarChangeListener(this);
|
||||
mPreferenceSeekBar.setMax(max);
|
||||
mPreferenceSeekBar.setProgress(Math.min(seekBarCurrentValue, max));
|
||||
mPreferenceSeekBar.setKeyProgressIncrement(increment);
|
||||
mPreferenceSeekBar.setPadding(0, 0, 0, 10);
|
||||
// add the seek bar to the layout
|
||||
linearLayout.addView(this.preferenceSeekBar);
|
||||
linearLayout.addView(mPreferenceSeekBar);
|
||||
|
||||
// create the text view for the current value below the seek bar
|
||||
this.currentValueTextView = new TextView(getContext());
|
||||
this.currentValueTextView.setText(getCurrentValueText(this.preferenceSeekBar.getProgress()));
|
||||
this.currentValueTextView.setGravity(Gravity.CENTER_HORIZONTAL);
|
||||
mCurrentValueTextView = new TextView(getContext());
|
||||
mCurrentValueTextView.setText(getCurrentValueText(mPreferenceSeekBar.getProgress()));
|
||||
mCurrentValueTextView.setGravity(Gravity.CENTER_HORIZONTAL);
|
||||
// add the current value text view to the layout
|
||||
linearLayout.addView(this.currentValueTextView);
|
||||
linearLayout.addView(mCurrentValueTextView);
|
||||
|
||||
return linearLayout;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current value text.
|
||||
*
|
||||
* @param progress
|
||||
* the current progress level of the seek bar.
|
||||
* @return the new current value text
|
||||
|
||||
@ -16,40 +16,46 @@ package org.oscim.core;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.ObjectInputStream;
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
|
||||
/**
|
||||
* A BoundingBox represents an immutable set of two latitude and two longitude coordinates.
|
||||
* A BoundingBox represents an immutable set of two latitude and two longitude
|
||||
* coordinates.
|
||||
*/
|
||||
public class BoundingBox implements Serializable {
|
||||
public class BoundingBox implements Parcelable {
|
||||
/**
|
||||
* Conversion factor from degrees to microdegrees.
|
||||
*/
|
||||
private static final double CONVERSION_FACTOR = 1000000d;
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private static boolean isBetween(int number, int min, int max) {
|
||||
return min <= number && number <= max;
|
||||
}
|
||||
|
||||
/**
|
||||
* The maximum latitude value of this BoundingBox in microdegrees (degrees * 10^6).
|
||||
* The maximum latitude value of this BoundingBox in microdegrees (degrees *
|
||||
* 10^6).
|
||||
*/
|
||||
public final int maxLatitudeE6;
|
||||
|
||||
/**
|
||||
* The maximum longitude value of this BoundingBox in microdegrees (degrees * 10^6).
|
||||
* The maximum longitude value of this BoundingBox in microdegrees (degrees
|
||||
* * 10^6).
|
||||
*/
|
||||
public final int maxLongitudeE6;
|
||||
|
||||
/**
|
||||
* The minimum latitude value of this BoundingBox in microdegrees (degrees * 10^6).
|
||||
* The minimum latitude value of this BoundingBox in microdegrees (degrees *
|
||||
* 10^6).
|
||||
*/
|
||||
public final int minLatitudeE6;
|
||||
|
||||
/**
|
||||
* The minimum longitude value of this BoundingBox in microdegrees (degrees * 10^6).
|
||||
* The minimum longitude value of this BoundingBox in microdegrees (degrees
|
||||
* * 10^6).
|
||||
*/
|
||||
public final int minLongitudeE6;
|
||||
|
||||
@ -76,10 +82,20 @@ public class BoundingBox implements Serializable {
|
||||
this.hashCodeValue = calculateHashCode();
|
||||
}
|
||||
|
||||
public BoundingBox(double minLatitude, double minLongitude, double maxLatitude,
|
||||
double maxLongitude) {
|
||||
this.minLatitudeE6 = (int) (minLatitude * 1E6);
|
||||
this.minLongitudeE6 = (int) (minLongitude * 1E6);
|
||||
this.maxLatitudeE6 = (int) (maxLatitude * 1E6);
|
||||
this.maxLongitudeE6 = (int) (maxLongitude * 1E6);
|
||||
this.hashCodeValue = calculateHashCode();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param geoPoint
|
||||
* the point whose coordinates should be checked.
|
||||
* @return true if this BoundingBox contains the given GeoPoint, false otherwise.
|
||||
* @return true if this BoundingBox contains the given GeoPoint, false
|
||||
* otherwise.
|
||||
*/
|
||||
public boolean contains(GeoPoint geoPoint) {
|
||||
return isBetween(geoPoint.latitudeE6, this.minLatitudeE6, this.maxLatitudeE6)
|
||||
@ -107,12 +123,14 @@ public class BoundingBox implements Serializable {
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the GeoPoint at the horizontal and vertical center of this BoundingBox.
|
||||
* @return the GeoPoint at the horizontal and vertical center of this
|
||||
* BoundingBox.
|
||||
*/
|
||||
public GeoPoint getCenterPoint() {
|
||||
int latitudeOffset = (this.maxLatitudeE6 - this.minLatitudeE6) / 2;
|
||||
int longitudeOffset = (this.maxLongitudeE6 - this.minLongitudeE6) / 2;
|
||||
return new GeoPoint(this.minLatitudeE6 + latitudeOffset, this.minLongitudeE6 + longitudeOffset);
|
||||
return new GeoPoint(this.minLatitudeE6 + latitudeOffset, this.minLongitudeE6
|
||||
+ longitudeOffset);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -175,8 +193,78 @@ public class BoundingBox implements Serializable {
|
||||
return result;
|
||||
}
|
||||
|
||||
private void readObject(ObjectInputStream objectInputStream) throws IOException, ClassNotFoundException {
|
||||
private void readObject(ObjectInputStream objectInputStream) throws IOException,
|
||||
ClassNotFoundException {
|
||||
objectInputStream.defaultReadObject();
|
||||
this.hashCodeValue = calculateHashCode();
|
||||
}
|
||||
|
||||
/* code below is from osdmroid, @author Nicolas Gramlich */
|
||||
|
||||
public static BoundingBox fromGeoPoints(final ArrayList<? extends GeoPoint> partialPolyLine) {
|
||||
int minLat = Integer.MAX_VALUE;
|
||||
int minLon = Integer.MAX_VALUE;
|
||||
int maxLat = Integer.MIN_VALUE;
|
||||
int maxLon = Integer.MIN_VALUE;
|
||||
for (final GeoPoint gp : partialPolyLine) {
|
||||
|
||||
minLat = Math.min(minLat, gp.latitudeE6);
|
||||
minLon = Math.min(minLon, gp.longitudeE6);
|
||||
maxLat = Math.max(maxLat, gp.latitudeE6);
|
||||
maxLon = Math.max(maxLon, gp.longitudeE6);
|
||||
}
|
||||
|
||||
return new BoundingBox(minLat, minLon, maxLat, maxLon);
|
||||
}
|
||||
|
||||
public static final Parcelable.Creator<BoundingBox> CREATOR = new Parcelable.Creator<BoundingBox>() {
|
||||
@Override
|
||||
public BoundingBox createFromParcel(final Parcel in) {
|
||||
return new BoundingBox(in.readInt(), in.readInt(), in.readInt(), in.readInt());
|
||||
}
|
||||
|
||||
@Override
|
||||
public BoundingBox[] newArray(final int size) {
|
||||
return new BoundingBox[size];
|
||||
}
|
||||
};
|
||||
|
||||
@Override
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeToParcel(Parcel dest, int flags) {
|
||||
dest.writeInt(minLatitudeE6);
|
||||
dest.writeInt(minLongitudeE6);
|
||||
dest.writeInt(maxLatitudeE6);
|
||||
dest.writeInt(maxLongitudeE6);
|
||||
}
|
||||
|
||||
// public BoundingBox(final double north, final double east, final double south,
|
||||
// final double west) {
|
||||
// this((int) (north * 1E6), (int) (east * 1E6), (int) (south * 1E6), (int) (west * 1E6));
|
||||
// // this.mLatNorthE6 = (int) (north * 1E6);
|
||||
// // this.mLonEastE6 = (int) (east * 1E6);
|
||||
// // this.mLatSouthE6 = (int) (south * 1E6);
|
||||
// // this.mLonWestE6 = (int) (west * 1E6);
|
||||
// }
|
||||
|
||||
// public int getLatNorthE6() {
|
||||
// return this.maxLatitudeE6;
|
||||
// }
|
||||
//
|
||||
// public int getLatSouthE6() {
|
||||
// return this.minLatitudeE6;
|
||||
// }
|
||||
//
|
||||
// public int getLonEastE6() {
|
||||
// return this.maxLongitudeE6;
|
||||
// }
|
||||
//
|
||||
// public int getLonWestE6() {
|
||||
// return this.minLongitudeE6;
|
||||
// }
|
||||
|
||||
}
|
||||
|
||||
@ -1,6 +1,8 @@
|
||||
/*
|
||||
* Copyright 2010, 2011, 2012 mapsforge.org
|
||||
*
|
||||
* Copyright 2012 osmdroid authors: Nicolas Gramlich, Theodore Hong
|
||||
* Copyright 2012 Hannes Janetzek
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it under the
|
||||
* terms of the GNU Lesser General Public License as published by the Free Software
|
||||
* Foundation, either version 3 of the License, or (at your option) any later version.
|
||||
@ -14,10 +16,14 @@
|
||||
*/
|
||||
package org.oscim.core;
|
||||
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
|
||||
/**
|
||||
* A GeoPoint represents an immutable pair of latitude and longitude coordinates.
|
||||
* A GeoPoint represents an immutable pair of latitude and longitude
|
||||
* coordinates.
|
||||
*/
|
||||
public class GeoPoint implements Comparable<GeoPoint> {
|
||||
public class GeoPoint implements Parcelable, Comparable<GeoPoint> {
|
||||
/**
|
||||
* Conversion factor from degrees to microdegrees.
|
||||
*/
|
||||
@ -33,6 +39,8 @@ public class GeoPoint implements Comparable<GeoPoint> {
|
||||
*/
|
||||
public final int longitudeE6;
|
||||
|
||||
// public final int altitude;
|
||||
|
||||
/**
|
||||
* The hash code of this object.
|
||||
*/
|
||||
@ -40,9 +48,11 @@ public class GeoPoint implements Comparable<GeoPoint> {
|
||||
|
||||
/**
|
||||
* @param latitude
|
||||
* the latitude in degrees, will be limited to the possible latitude range.
|
||||
* the latitude in degrees, will be limited to the possible
|
||||
* latitude range.
|
||||
* @param longitude
|
||||
* the longitude in degrees, will be limited to the possible longitude range.
|
||||
* the longitude in degrees, will be limited to the possible
|
||||
* longitude range.
|
||||
*/
|
||||
public GeoPoint(double latitude, double longitude) {
|
||||
double limitLatitude = MercatorProjection.limitLatitude(latitude);
|
||||
@ -50,14 +60,15 @@ public class GeoPoint implements Comparable<GeoPoint> {
|
||||
|
||||
double limitLongitude = MercatorProjection.limitLongitude(longitude);
|
||||
this.longitudeE6 = (int) (limitLongitude * CONVERSION_FACTOR);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param latitudeE6
|
||||
* the latitude in microdegrees (degrees * 10^6), will be limited to the possible latitude range.
|
||||
* the latitude in microdegrees (degrees * 10^6), will be limited
|
||||
* to the possible latitude range.
|
||||
* @param longitudeE6
|
||||
* the longitude in microdegrees (degrees * 10^6), will be limited to the possible longitude range.
|
||||
* the longitude in microdegrees (degrees * 10^6), will be
|
||||
* limited to the possible longitude range.
|
||||
*/
|
||||
public GeoPoint(int latitudeE6, int longitudeE6) {
|
||||
this(latitudeE6 / CONVERSION_FACTOR, longitudeE6 / CONVERSION_FACTOR);
|
||||
@ -135,4 +146,66 @@ public class GeoPoint implements Comparable<GeoPoint> {
|
||||
result = 31 * result + this.longitudeE6;
|
||||
return result;
|
||||
}
|
||||
|
||||
// ===========================================================
|
||||
// Methods from osmdroid
|
||||
// ===========================================================
|
||||
|
||||
public static final float DEG2RAD = (float) (Math.PI / 180.0);
|
||||
public static final float RAD2DEG = (float) (180.0 / Math.PI);
|
||||
// http://en.wikipedia.org/wiki/Earth_radius#Equatorial_radius
|
||||
public static final int RADIUS_EARTH_METERS = 6378137;
|
||||
|
||||
/**
|
||||
* @see "http://www.geocities.com/DrChengalva/GPSDistance.html"
|
||||
* @param other
|
||||
* ...
|
||||
* @return distance in meters
|
||||
*/
|
||||
public int distanceTo(final GeoPoint other) {
|
||||
|
||||
double a1 = DEG2RAD * latitudeE6 / 1E6;
|
||||
double a2 = DEG2RAD * longitudeE6 / 1E6;
|
||||
double b1 = DEG2RAD * other.latitudeE6 / 1E6;
|
||||
double b2 = DEG2RAD * other.longitudeE6 / 1E6;
|
||||
|
||||
double cosa1 = Math.cos(a1);
|
||||
double cosb1 = Math.cos(b1);
|
||||
|
||||
double t1 = cosa1 * Math.cos(a2) * cosb1 * Math.cos(b2);
|
||||
double t2 = cosa1 * Math.sin(a2) * cosb1 * Math.sin(b2);
|
||||
|
||||
double t3 = Math.sin(a1) * Math.sin(b1);
|
||||
|
||||
double tt = Math.acos(t1 + t2 + t3);
|
||||
|
||||
return (int) (RADIUS_EARTH_METERS * tt);
|
||||
}
|
||||
|
||||
// ===========================================================
|
||||
// Parcelable
|
||||
// ===========================================================
|
||||
|
||||
@Override
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeToParcel(final Parcel out, final int flags) {
|
||||
out.writeInt(latitudeE6);
|
||||
out.writeInt(longitudeE6);
|
||||
}
|
||||
|
||||
public static final Parcelable.Creator<GeoPoint> CREATOR = new Parcelable.Creator<GeoPoint>() {
|
||||
@Override
|
||||
public GeoPoint createFromParcel(final Parcel in) {
|
||||
return new GeoPoint(in.readInt(), in.readInt());
|
||||
}
|
||||
|
||||
@Override
|
||||
public GeoPoint[] newArray(final int size) {
|
||||
return new GeoPoint[size];
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
/*
|
||||
* Copyright 2010, 2011, 2012 mapsforge.org
|
||||
* Copyright 2012 Hannes Janetzek
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it under the
|
||||
* terms of the GNU Lesser General Public License as published by the Free Software
|
||||
@ -16,9 +17,7 @@ package org.oscim.core;
|
||||
|
||||
import android.opengl.Matrix;
|
||||
|
||||
/**
|
||||
* A MapPosition Container.
|
||||
*/
|
||||
/** A MapPosition Container. */
|
||||
public class MapPosition {
|
||||
|
||||
public double lon;
|
||||
@ -33,7 +32,9 @@ public class MapPosition {
|
||||
public double y;
|
||||
|
||||
public float[] viewMatrix;
|
||||
public float[] rotateMatrix;
|
||||
|
||||
// // DO NOT MODIFY! shared with MapViewPosition
|
||||
// public float[] projMatrix;
|
||||
|
||||
public MapPosition() {
|
||||
this.zoomLevel = (byte) 1;
|
||||
@ -49,19 +50,32 @@ public class MapPosition {
|
||||
public void init() {
|
||||
viewMatrix = new float[16];
|
||||
Matrix.setIdentityM(viewMatrix, 0);
|
||||
|
||||
rotateMatrix = new float[16];
|
||||
Matrix.setIdentityM(rotateMatrix, 0);
|
||||
//
|
||||
// rotateMatrix = new float[16];
|
||||
// Matrix.setIdentityM(rotateMatrix, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param geoPoint
|
||||
// public Point geopointToMap(GeoPoint in, Point reuse) {
|
||||
// Point out = reuse == null ? new Point() : reuse;
|
||||
// out.x = (int) (MercatorProjection.longitudeToPixelX(in.getLongitude(), zoomLevel) - x);
|
||||
// out.y = (int) (MercatorProjection.latitudeToPixelY(in.getLatitude(), zoomLevel) - y);
|
||||
//
|
||||
// return out;
|
||||
// }
|
||||
|
||||
// public void geopointToMap(GeoPoint in, float[] out, int pos) {
|
||||
// out[pos * 2 + 0] =
|
||||
// (float) (MercatorProjection.longitudeToPixelX(in.getLongitude(), zoomLevel) - x);
|
||||
// out[pos * 2 + 1] =
|
||||
// (float) (MercatorProjection.latitudeToPixelY(in.getLatitude(), zoomLevel) - y);
|
||||
// }
|
||||
|
||||
/** @param geoPoint
|
||||
* the map position.
|
||||
* @param zoomLevel
|
||||
* the zoom level.
|
||||
* @param scale
|
||||
* ...
|
||||
*/
|
||||
* ... */
|
||||
public MapPosition(GeoPoint geoPoint, byte zoomLevel, float scale) {
|
||||
this.zoomLevel = zoomLevel;
|
||||
this.scale = scale;
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
/*
|
||||
* Copyright 2010, 2011, 2012 mapsforge.org
|
||||
* Copyright 2012 Hannes Janetzek
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it under the
|
||||
* terms of the GNU Lesser General Public License as published by the Free Software
|
||||
@ -14,6 +15,7 @@
|
||||
*/
|
||||
package org.oscim.core;
|
||||
|
||||
import android.graphics.Point;
|
||||
|
||||
/**
|
||||
* An implementation of the spherical Mercator projection.
|
||||
@ -246,4 +248,13 @@ public final class MercatorProjection {
|
||||
private MercatorProjection() {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
|
||||
public static Point projectPoint(GeoPoint geopoint, byte z, Point reuse) {
|
||||
Point out = reuse == null ? new Point() : reuse;
|
||||
|
||||
out.x = (int) MercatorProjection.longitudeToPixelX(geopoint.getLongitude(), z);
|
||||
out.y = (int) MercatorProjection.latitudeToPixelY(geopoint.getLatitude(), z);
|
||||
|
||||
return out;
|
||||
}
|
||||
}
|
||||
|
||||
@ -38,7 +38,6 @@ public class JobQueue {
|
||||
* the job to be added to this queue.
|
||||
*/
|
||||
public synchronized void setJobs(ArrayList<JobTile> tiles) {
|
||||
// mPriorityQueue.clear();
|
||||
|
||||
for (int i = 0, n = tiles.size(); i < n; i++) {
|
||||
JobTile tile = tiles.get(i);
|
||||
@ -51,9 +50,10 @@ public class JobQueue {
|
||||
* Removes all jobs from this queue.
|
||||
*/
|
||||
public synchronized void clear() {
|
||||
for (JobTile tile : mPriorityQueue) {
|
||||
tile.isLoading = false;
|
||||
}
|
||||
|
||||
JobTile t;
|
||||
while ((t = mPriorityQueue.poll()) != null)
|
||||
t.isLoading = false;
|
||||
|
||||
mPriorityQueue.clear();
|
||||
}
|
||||
|
||||
159
src/org/oscim/overlay/DefaultResourceProxyImpl.java
Normal file
@ -0,0 +1,159 @@
|
||||
package org.oscim.overlay;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.lang.reflect.Field;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.BitmapFactory;
|
||||
import android.graphics.drawable.BitmapDrawable;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.util.DisplayMetrics;
|
||||
|
||||
public class DefaultResourceProxyImpl implements ResourceProxy, MapViewConstants {
|
||||
// private static final String TAG = DefaultResourceProxyImpl.class.getSimpleName();
|
||||
|
||||
private DisplayMetrics mDisplayMetrics;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param pContext
|
||||
* Used to get the display metrics that are used for scaling the
|
||||
* bitmaps returned by {@@link getBitmap}. Can be null,
|
||||
* in which case the bitmaps are not scaled.
|
||||
*/
|
||||
public DefaultResourceProxyImpl(final Context pContext) {
|
||||
if (pContext != null) {
|
||||
mDisplayMetrics = pContext.getResources().getDisplayMetrics();
|
||||
// if (DEBUGMODE) {
|
||||
// logger.debug("mDisplayMetrics=" + mDisplayMetrics);
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getString(final string pResId) {
|
||||
switch (pResId) {
|
||||
case mapnik:
|
||||
return "Mapnik";
|
||||
case cyclemap:
|
||||
return "Cycle Map";
|
||||
case public_transport:
|
||||
return "Public transport";
|
||||
case base:
|
||||
return "OSM base layer";
|
||||
case topo:
|
||||
return "Topographic";
|
||||
case hills:
|
||||
return "Hills";
|
||||
case cloudmade_standard:
|
||||
return "CloudMade (Standard tiles)";
|
||||
case cloudmade_small:
|
||||
return "CloudMade (small tiles)";
|
||||
case mapquest_osm:
|
||||
return "Mapquest";
|
||||
case mapquest_aerial:
|
||||
return "Mapquest Aerial";
|
||||
case bing:
|
||||
return "Bing";
|
||||
case fiets_nl:
|
||||
return "OpenFietsKaart overlay";
|
||||
case base_nl:
|
||||
return "Netherlands base overlay";
|
||||
case roads_nl:
|
||||
return "Netherlands roads overlay";
|
||||
case unknown:
|
||||
return "Unknown";
|
||||
case format_distance_meters:
|
||||
return "%s m";
|
||||
case format_distance_kilometers:
|
||||
return "%s km";
|
||||
case format_distance_miles:
|
||||
return "%s mi";
|
||||
case format_distance_nautical_miles:
|
||||
return "%s nm";
|
||||
case format_distance_feet:
|
||||
return "%s ft";
|
||||
case online_mode:
|
||||
return "Online mode";
|
||||
case offline_mode:
|
||||
return "Offline mode";
|
||||
case my_location:
|
||||
return "My location";
|
||||
case compass:
|
||||
return "Compass";
|
||||
case map_mode:
|
||||
return "Map mode";
|
||||
default:
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getString(final string pResId, final Object... formatArgs) {
|
||||
return String.format(getString(pResId), formatArgs);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Bitmap getBitmap(final bitmap pResId) {
|
||||
InputStream is = null;
|
||||
try {
|
||||
final String resName = pResId.name() + ".png";
|
||||
is = getClass().getResourceAsStream(resName);
|
||||
if (is == null) {
|
||||
throw new IllegalArgumentException("Resource not found: " + resName);
|
||||
}
|
||||
BitmapFactory.Options options = null;
|
||||
if (mDisplayMetrics != null) {
|
||||
options = getBitmapOptions();
|
||||
}
|
||||
return BitmapFactory.decodeStream(is, null, options);
|
||||
} catch (final OutOfMemoryError e) {
|
||||
// logger.error("OutOfMemoryError getting bitmap resource: " +
|
||||
// pResId);
|
||||
System.gc();
|
||||
// there's not much we can do here
|
||||
// - when we load a bitmap from resources we expect it to be found
|
||||
throw e;
|
||||
} finally {
|
||||
if (is != null) {
|
||||
try {
|
||||
is.close();
|
||||
} catch (final IOException ignore) {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private BitmapFactory.Options getBitmapOptions() {
|
||||
try {
|
||||
final Field density = DisplayMetrics.class.getDeclaredField("DENSITY_DEFAULT");
|
||||
final Field inDensity = BitmapFactory.Options.class.getDeclaredField("inDensity");
|
||||
final Field inTargetDensity = BitmapFactory.Options.class
|
||||
.getDeclaredField("inTargetDensity");
|
||||
final Field targetDensity = DisplayMetrics.class.getDeclaredField("densityDpi");
|
||||
final BitmapFactory.Options options = new BitmapFactory.Options();
|
||||
inDensity.setInt(options, density.getInt(null));
|
||||
inTargetDensity.setInt(options, targetDensity.getInt(mDisplayMetrics));
|
||||
return options;
|
||||
} catch (final IllegalAccessException ex) {
|
||||
// ignore
|
||||
} catch (final NoSuchFieldException ex) {
|
||||
// ignore
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Drawable getDrawable(final bitmap pResId) {
|
||||
return new BitmapDrawable(getBitmap(pResId));
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getDisplayMetricsDensity() {
|
||||
return mDisplayMetrics.density;
|
||||
}
|
||||
|
||||
}
|
||||
@ -12,21 +12,20 @@
|
||||
* You should have received a copy of the GNU Lesser General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.oscim.view;
|
||||
package org.oscim.overlay;
|
||||
|
||||
import android.os.Handler;
|
||||
import android.os.Message;
|
||||
import org.oscim.renderer.overlays.RenderOverlay;
|
||||
import org.oscim.view.MapView;
|
||||
|
||||
public class DelayedTaskHandler extends Handler {
|
||||
public final int MESSAGE_UPDATE_POSITION = 1;
|
||||
|
||||
@Override
|
||||
public void handleMessage(Message msg) {
|
||||
switch (msg.what) {
|
||||
case MESSAGE_UPDATE_POSITION:
|
||||
|
||||
break;
|
||||
|
||||
}
|
||||
public class GenericOverlay extends Overlay {
|
||||
/**
|
||||
* @param mapView
|
||||
* ...
|
||||
* @param renderer
|
||||
* ...
|
||||
*/
|
||||
public GenericOverlay(MapView mapView, RenderOverlay renderer) {
|
||||
super();
|
||||
mLayer = renderer;
|
||||
}
|
||||
}
|
||||
278
src/org/oscim/overlay/ItemizedIconOverlay.java
Normal file
@ -0,0 +1,278 @@
|
||||
package org.oscim.overlay;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.oscim.core.MercatorProjection;
|
||||
import org.oscim.overlay.ResourceProxy.bitmap;
|
||||
import org.oscim.view.MapView;
|
||||
import org.oscim.view.MapViewPosition;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Point;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.util.FloatMath;
|
||||
import android.util.Log;
|
||||
import android.view.MotionEvent;
|
||||
|
||||
public class ItemizedIconOverlay<Item extends OverlayItem> extends ItemizedOverlay<Item> {
|
||||
private static final String TAG = ItemizedIconOverlay.class.getSimpleName();
|
||||
|
||||
protected final List<Item> mItemList;
|
||||
protected OnItemGestureListener<Item> mOnItemGestureListener;
|
||||
private int mDrawnItemsLimit = Integer.MAX_VALUE;
|
||||
private final Point mTouchScreenPoint = new Point();
|
||||
|
||||
private final Point mItemPoint = new Point();
|
||||
|
||||
public ItemizedIconOverlay(
|
||||
final MapView mapView,
|
||||
final List<Item> pList,
|
||||
final Drawable pDefaultMarker,
|
||||
final ItemizedIconOverlay.OnItemGestureListener<Item> pOnItemGestureListener,
|
||||
final ResourceProxy pResourceProxy) {
|
||||
|
||||
super(mapView, pDefaultMarker, pResourceProxy);
|
||||
|
||||
this.mItemList = pList;
|
||||
this.mOnItemGestureListener = pOnItemGestureListener;
|
||||
populate();
|
||||
}
|
||||
|
||||
public ItemizedIconOverlay(
|
||||
final MapView mapView,
|
||||
final List<Item> pList,
|
||||
final ItemizedIconOverlay.OnItemGestureListener<Item> pOnItemGestureListener,
|
||||
final ResourceProxy pResourceProxy) {
|
||||
|
||||
this(mapView, pList, pResourceProxy.getDrawable(bitmap.marker_default),
|
||||
pOnItemGestureListener,
|
||||
pResourceProxy);
|
||||
}
|
||||
|
||||
public ItemizedIconOverlay(
|
||||
final MapView mapView,
|
||||
final Context pContext,
|
||||
final List<Item> pList,
|
||||
final ItemizedIconOverlay.OnItemGestureListener<Item> pOnItemGestureListener) {
|
||||
this(mapView, pList, new DefaultResourceProxyImpl(pContext)
|
||||
.getDrawable(bitmap.marker_default),
|
||||
pOnItemGestureListener, new DefaultResourceProxyImpl(pContext));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onSnapToItem(final int pX, final int pY, final Point pSnapPoint,
|
||||
final MapView pMapView) {
|
||||
// TODO Implement this!
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Item createItem(final int index) {
|
||||
return mItemList.get(index);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
return Math.min(mItemList.size(), mDrawnItemsLimit);
|
||||
}
|
||||
|
||||
public boolean addItem(final Item item) {
|
||||
final boolean result = mItemList.add(item);
|
||||
populate();
|
||||
return result;
|
||||
}
|
||||
|
||||
public void addItem(final int location, final Item item) {
|
||||
mItemList.add(location, item);
|
||||
}
|
||||
|
||||
public boolean addItems(final List<Item> items) {
|
||||
final boolean result = mItemList.addAll(items);
|
||||
populate();
|
||||
return result;
|
||||
}
|
||||
|
||||
public void removeAllItems() {
|
||||
removeAllItems(true);
|
||||
}
|
||||
|
||||
public void removeAllItems(final boolean withPopulate) {
|
||||
mItemList.clear();
|
||||
if (withPopulate) {
|
||||
populate();
|
||||
}
|
||||
}
|
||||
|
||||
public boolean removeItem(final Item item) {
|
||||
final boolean result = mItemList.remove(item);
|
||||
populate();
|
||||
return result;
|
||||
}
|
||||
|
||||
public Item removeItem(final int position) {
|
||||
final Item result = mItemList.remove(position);
|
||||
populate();
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Each of these methods performs a item sensitive check. If the item is
|
||||
* located its corresponding method is called. The result of the call is
|
||||
* returned. Helper methods are provided so that child classes may more
|
||||
* easily override behavior without resorting to overriding the
|
||||
* ItemGestureListener methods.
|
||||
*/
|
||||
@Override
|
||||
public boolean onSingleTapUp(final MotionEvent event, final MapView mapView) {
|
||||
return (activateSelectedItems(event, mapView, new ActiveItem() {
|
||||
@Override
|
||||
public boolean run(final int index) {
|
||||
final ItemizedIconOverlay<Item> that = ItemizedIconOverlay.this;
|
||||
if (that.mOnItemGestureListener == null) {
|
||||
return false;
|
||||
}
|
||||
return onSingleTapUpHelper(index, that.mItemList.get(index), mapView);
|
||||
}
|
||||
})) || super.onSingleTapUp(event, mapView);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param index
|
||||
* ...
|
||||
* @param item
|
||||
* ...
|
||||
* @param mapView
|
||||
* ...
|
||||
* @return ...
|
||||
*/
|
||||
protected boolean onSingleTapUpHelper(final int index, final Item item, final MapView mapView) {
|
||||
return this.mOnItemGestureListener.onItemSingleTapUp(index, item);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onLongPress(final MotionEvent event, final MapView mapView) {
|
||||
|
||||
Log.d(TAG, "onLongPress");
|
||||
|
||||
return (activateSelectedItems(event, mapView, new ActiveItem() {
|
||||
@Override
|
||||
public boolean run(final int index) {
|
||||
final ItemizedIconOverlay<Item> that = ItemizedIconOverlay.this;
|
||||
if (that.mOnItemGestureListener == null) {
|
||||
return false;
|
||||
}
|
||||
return onLongPressHelper(index, getItem(index));
|
||||
}
|
||||
})) || super.onLongPress(event, mapView);
|
||||
}
|
||||
|
||||
protected boolean onLongPressHelper(final int index, final Item item) {
|
||||
return this.mOnItemGestureListener.onItemLongPress(index, item);
|
||||
}
|
||||
|
||||
/**
|
||||
* When a content sensitive action is performed the content item needs to be
|
||||
* identified. This method does that and then performs the assigned task on
|
||||
* that item.
|
||||
*
|
||||
* @param event
|
||||
* ...
|
||||
* @param mapView
|
||||
* ...
|
||||
* @param task
|
||||
* ..
|
||||
* @return true if event is handled false otherwise
|
||||
*/
|
||||
private boolean activateSelectedItems(final MotionEvent event, final MapView mapView,
|
||||
final ActiveItem task) {
|
||||
|
||||
// final Projection pj = mapView.getProjection();
|
||||
final int eventX = (int) event.getX();
|
||||
final int eventY = (int) event.getY();
|
||||
|
||||
// Log.d("...", "test items " + eventX + " " + eventY);
|
||||
|
||||
/* These objects are created to avoid construct new ones every cycle. */
|
||||
// pj.fromMapPixels(eventX, eventY, mTouchScreenPoint);
|
||||
|
||||
MapViewPosition mapViewPosition = mMapView.getMapViewPosition();
|
||||
|
||||
byte z = mapViewPosition.getMapPosition().zoomLevel;
|
||||
|
||||
mapViewPosition.getScreenPointOnMap(eventX, eventY, mTouchScreenPoint);
|
||||
|
||||
int nearest = -1;
|
||||
float dist = Float.MAX_VALUE;
|
||||
|
||||
// TODO use intermediate projection and bounding box test
|
||||
|
||||
for (int i = 0; i < this.mItemList.size(); ++i) {
|
||||
final Item item = getItem(i);
|
||||
|
||||
// final Drawable marker = (item.getMarker(0) == null) ? this.mDefaultMarker : item
|
||||
// .getMarker(0);
|
||||
|
||||
// int x = (int) MercatorProjection.longitudeToPixelX(item.getPoint().getLongitude(), z);
|
||||
// int y = (int) MercatorProjection.latitudeToPixelY(item.getPoint().getLatitude(), z);
|
||||
MercatorProjection.projectPoint(item.getPoint(), z, mItemPoint);
|
||||
|
||||
// pj.toPixels(item.getPoint(), mItemPoint);
|
||||
// Log.d("...", (x - mTouchScreenPoint.x) + " " + (y - mTouchScreenPoint.y));
|
||||
|
||||
float dx = mItemPoint.x - mTouchScreenPoint.x;
|
||||
float dy = mItemPoint.y - mTouchScreenPoint.y;
|
||||
float d = FloatMath.sqrt(dx * dx + dy * dy);
|
||||
|
||||
if (d < 50) {
|
||||
// Log.d("...", "HIT! " + (x - mTouchScreenPoint.x) + " " + (y - mTouchScreenPoint.y));
|
||||
if (d < dist) {
|
||||
dist = d;
|
||||
nearest = i;
|
||||
}
|
||||
// if (hitTest(item, marker, mTouchScreenPoint.x - mItemPoint.x, mTouchScreenPoint.y
|
||||
// - mItemPoint.y)) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
if (nearest >= 0 && task.run(nearest)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// ===========================================================
|
||||
// Getter & Setter
|
||||
// ===========================================================
|
||||
|
||||
public int getDrawnItemsLimit() {
|
||||
return this.mDrawnItemsLimit;
|
||||
}
|
||||
|
||||
public void setDrawnItemsLimit(final int aLimit) {
|
||||
this.mDrawnItemsLimit = aLimit;
|
||||
}
|
||||
|
||||
// ===========================================================
|
||||
// Inner and Anonymous Classes
|
||||
// ===========================================================
|
||||
|
||||
/**
|
||||
* When the item is touched one of these methods may be invoked depending on
|
||||
* the type of touch. Each of them returns true if the event was completely
|
||||
* handled.
|
||||
*
|
||||
* @param <T>
|
||||
* ....
|
||||
*/
|
||||
public static interface OnItemGestureListener<T> {
|
||||
public boolean onItemSingleTapUp(final int index, final T item);
|
||||
|
||||
public boolean onItemLongPress(final int index, final T item);
|
||||
}
|
||||
|
||||
public static interface ActiveItem {
|
||||
public boolean run(final int aIndex);
|
||||
}
|
||||
}
|
||||
484
src/org/oscim/overlay/ItemizedOverlay.java
Normal file
@ -0,0 +1,484 @@
|
||||
// Created by plusminus on 23:18:23 - 02.10.2008
|
||||
package org.oscim.overlay;
|
||||
|
||||
import org.oscim.core.GeoPoint;
|
||||
import org.oscim.core.MapPosition;
|
||||
import org.oscim.core.MercatorProjection;
|
||||
import org.oscim.overlay.OverlayItem.HotspotPlace;
|
||||
import org.oscim.renderer.layer.SymbolLayer;
|
||||
import org.oscim.renderer.overlays.RenderOverlay;
|
||||
import org.oscim.view.MapView;
|
||||
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.opengl.Matrix;
|
||||
|
||||
/**
|
||||
* Draws a list of {@link OverlayItem} as markers to a map. The item with the
|
||||
* lowest index is drawn as last and therefore the 'topmost' marker. It also
|
||||
* gets checked for onTap first. This class is generic, because you then you get
|
||||
* your custom item-class passed back in onTap().
|
||||
* @author Marc Kurtz
|
||||
* @author Nicolas Gramlich
|
||||
* @author Theodore Hong
|
||||
* @author Fred Eisele
|
||||
* @author Hannes Janetzek
|
||||
* @param <Item>
|
||||
* ...
|
||||
*/
|
||||
public abstract class ItemizedOverlay<Item extends OverlayItem> extends Overlay implements
|
||||
Overlay.Snappable {
|
||||
|
||||
protected final Drawable mDefaultMarker;
|
||||
protected boolean mDrawFocusedItem = true;
|
||||
protected MapView mMapView;
|
||||
|
||||
class InternalItem {
|
||||
InternalItem next;
|
||||
|
||||
Item item;
|
||||
boolean visible;
|
||||
boolean changes;
|
||||
int x, y, px, py;
|
||||
}
|
||||
|
||||
/* package */InternalItem mItems;
|
||||
/* package */Object lock = new Object();
|
||||
// /* package */final ArrayList<Item> mInternalItemList;
|
||||
private final Rect mRect = new Rect();
|
||||
/* package */Item mFocusedItem;
|
||||
/* package */boolean mUpdate;
|
||||
|
||||
private int mSize;
|
||||
|
||||
// pre-projected points to zoomlovel 20
|
||||
private static final byte MAX_ZOOM = 20;
|
||||
|
||||
class ItemOverlay extends RenderOverlay {
|
||||
|
||||
private SymbolLayer mSymbolLayer;
|
||||
private float[] mMvp = new float[16];
|
||||
private float[] mVec = new float[4];
|
||||
|
||||
public ItemOverlay(MapView mapView) {
|
||||
super(mapView);
|
||||
mSymbolLayer = new SymbolLayer();
|
||||
}
|
||||
|
||||
// note: this is called from GL-Thread. so check your syncs!
|
||||
@Override
|
||||
public synchronized void update(MapPosition curPos, boolean positionChanged,
|
||||
boolean tilesChanged) {
|
||||
|
||||
if (!tilesChanged && !mUpdate)
|
||||
return;
|
||||
|
||||
mUpdate = false;
|
||||
|
||||
int diff = MAX_ZOOM - curPos.zoomLevel;
|
||||
int mx = (int) curPos.x;
|
||||
int my = (int) curPos.y;
|
||||
|
||||
// TODO could pass mvp as param
|
||||
mMapView.getMapViewPosition().getMVP(mMvp);
|
||||
|
||||
float[] matrix = mMvp;
|
||||
float[] vec = mVec;
|
||||
|
||||
// limit could be 1 if we update on every position change
|
||||
float limit = 1.5f;
|
||||
|
||||
// no need to project these
|
||||
int max = (1 << 11);
|
||||
|
||||
int changesInvisible = 0;
|
||||
int changedVisible = 0;
|
||||
int numVisible = 0;
|
||||
|
||||
synchronized (lock) {
|
||||
|
||||
// check changes
|
||||
for (InternalItem it = mItems; it != null; it = it.next) {
|
||||
it.x = (it.px >> diff) - mx;
|
||||
it.y = (it.py >> diff) - my;
|
||||
|
||||
if (it.x > max || it.x < -max || it.y > max || it.y < -max) {
|
||||
if (it.visible) {
|
||||
it.changes = true;
|
||||
changesInvisible++;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
// map points to screen
|
||||
vec[0] = it.x;
|
||||
vec[1] = it.y;
|
||||
vec[2] = 0;
|
||||
vec[3] = 1;
|
||||
Matrix.multiplyMV(vec, 0, matrix, 0, vec, 0);
|
||||
float sx = vec[0] / vec[3];
|
||||
float sy = vec[1] / vec[3];
|
||||
|
||||
// check if it is visible
|
||||
if (sx < -limit || sx > limit || sy < -limit || sy > limit) {
|
||||
// Log.d("..", "outside " + it.x + " " + it.y + " -> " + sx + " " + sy);
|
||||
if (it.visible) {
|
||||
it.changes = true;
|
||||
changesInvisible++;
|
||||
}
|
||||
} else {
|
||||
if (!it.visible) {
|
||||
it.visible = true;
|
||||
changedVisible++;
|
||||
}
|
||||
it.changes = false;
|
||||
numVisible++;
|
||||
}
|
||||
}
|
||||
|
||||
// only update when zoomlevel changed, new items are visible
|
||||
// or more than 10 of the current items became invisible
|
||||
if (((curPos.zoomLevel == mMapPosition.zoomLevel || numVisible == 0)) &&
|
||||
(changedVisible == 0 && changesInvisible < 10))
|
||||
return;
|
||||
|
||||
// keep position for current state
|
||||
// updateMapPosition();
|
||||
// TODO add copy utility function
|
||||
mMapPosition.x = curPos.x;
|
||||
mMapPosition.y = curPos.y;
|
||||
mMapPosition.zoomLevel = curPos.zoomLevel;
|
||||
mMapPosition.scale = curPos.scale;
|
||||
mMapPosition.angle = curPos.angle;
|
||||
|
||||
// items are placed relative to scale == 1
|
||||
mMapPosition.scale = 1;
|
||||
|
||||
layers.clear();
|
||||
|
||||
for (InternalItem it = mItems; it != null; it = it.next) {
|
||||
if (!it.visible)
|
||||
continue;
|
||||
|
||||
if (it.changes) {
|
||||
it.visible = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
Item item = it.item; //mInternalItemList.get(i);
|
||||
|
||||
int state = 0;
|
||||
if (mDrawFocusedItem && (mFocusedItem == item))
|
||||
state = OverlayItem.ITEM_STATE_FOCUSED_MASK;
|
||||
|
||||
Drawable marker = item.getDrawable();
|
||||
if (marker == null)
|
||||
marker = mDefaultMarker;
|
||||
|
||||
// if (item.getMarker(state) == null) {
|
||||
// OverlayItem.setState(mDefaultMarker, state);
|
||||
// marker = mDefaultMarker;
|
||||
// } else
|
||||
// marker = item.getMarker(state);
|
||||
|
||||
boundToHotspot(marker, item.getMarkerHotspot());
|
||||
|
||||
mSymbolLayer.addDrawable(marker, state, it.x, it.y);
|
||||
}
|
||||
|
||||
}
|
||||
// Log.d("...", "changed " + changedVisible + " " + changesInvisible);
|
||||
mSymbolLayer.prepare();
|
||||
layers.textureLayers = mSymbolLayer;
|
||||
newData = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Method by which subclasses create the actual Items. This will only be
|
||||
* called from populate() we'll cache them for later use.
|
||||
* @param i
|
||||
* ...
|
||||
* @return ...
|
||||
*/
|
||||
protected abstract Item createItem(int i);
|
||||
|
||||
/**
|
||||
* The number of items in this overlay.
|
||||
* @return ...
|
||||
*/
|
||||
public abstract int size();
|
||||
|
||||
public ItemizedOverlay(MapView mapView, final Drawable pDefaultMarker, final ResourceProxy
|
||||
pResourceProxy) {
|
||||
|
||||
super(pResourceProxy);
|
||||
|
||||
if (pDefaultMarker == null) {
|
||||
throw new IllegalArgumentException("You must pass a default marker to ItemizedOverlay.");
|
||||
}
|
||||
|
||||
this.mDefaultMarker = pDefaultMarker;
|
||||
|
||||
// mInternalItemList = new ArrayList<Item>();
|
||||
|
||||
mMapView = mapView;
|
||||
mLayer = new ItemOverlay(mapView);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility method to perform all processing on a new ItemizedOverlay.
|
||||
* Subclasses provide Items through the createItem(int) method. The subclass
|
||||
* should call this as soon as it has data, before anything else gets
|
||||
* called.
|
||||
*/
|
||||
protected final void populate() {
|
||||
synchronized (lock) {
|
||||
final int size = size();
|
||||
mSize = size;
|
||||
|
||||
// reuse previous items
|
||||
InternalItem pool = mItems;
|
||||
mItems = null;
|
||||
|
||||
// flip order to draw in backward cycle, so the items
|
||||
// with the least index are on the front.
|
||||
for (int a = 0; a < size; a++) {
|
||||
InternalItem it;
|
||||
if (pool != null) {
|
||||
it = pool;
|
||||
it.visible = false;
|
||||
it.changes = false;
|
||||
pool = pool.next;
|
||||
} else {
|
||||
it = new InternalItem();
|
||||
}
|
||||
it.next = mItems;
|
||||
mItems = it;
|
||||
|
||||
it.item = createItem(a);
|
||||
|
||||
// pre-project points
|
||||
GeoPoint p = it.item.mGeoPoint;
|
||||
it.px = (int) MercatorProjection.longitudeToPixelX(p.getLongitude(), MAX_ZOOM);
|
||||
it.py = (int) MercatorProjection.latitudeToPixelY(p.getLatitude(), MAX_ZOOM);
|
||||
}
|
||||
mUpdate = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the Item at the given index.
|
||||
* @param position
|
||||
* the position of the item to return
|
||||
* @return the Item of the given index.
|
||||
*/
|
||||
public final Item getItem(final int position) {
|
||||
synchronized (lock) {
|
||||
InternalItem item = mItems;
|
||||
for (int i = mSize - position - 1; i > 0 && item != null; i--)
|
||||
item = item.next;
|
||||
|
||||
if (item != null)
|
||||
return item.item;
|
||||
|
||||
return null;
|
||||
}
|
||||
// return mInternalItemList.get(position);
|
||||
}
|
||||
|
||||
// private Drawable getDefaultMarker(final int state) {
|
||||
// OverlayItem.setState(mDefaultMarker, state);
|
||||
// return mDefaultMarker;
|
||||
// }
|
||||
|
||||
/**
|
||||
* See if a given hit point is within the bounds of an item's marker.
|
||||
* Override to modify the way an item is hit tested. The hit point is
|
||||
* relative to the marker's bounds. The default implementation just checks
|
||||
* to see if the hit point is within the touchable bounds of the marker.
|
||||
* @param item
|
||||
* the item to hit test
|
||||
* @param marker
|
||||
* the item's marker
|
||||
* @param hitX
|
||||
* x coordinate of point to check
|
||||
* @param hitY
|
||||
* y coordinate of point to check
|
||||
* @return true if the hit point is within the marker
|
||||
*/
|
||||
protected boolean hitTest(final Item item, final android.graphics.drawable.Drawable marker,
|
||||
final int hitX,
|
||||
final int hitY) {
|
||||
return marker.getBounds().contains(hitX, hitY);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set whether or not to draw the focused item. The default is to draw it,
|
||||
* but some clients may prefer to draw the focused item themselves.
|
||||
* @param drawFocusedItem
|
||||
* ...
|
||||
*/
|
||||
public void setDrawFocusedItem(final boolean drawFocusedItem) {
|
||||
mDrawFocusedItem = drawFocusedItem;
|
||||
}
|
||||
|
||||
/**
|
||||
* If the given Item is found in the overlay, force it to be the current
|
||||
* focus-bearer. Any registered {@@link
|
||||
* ItemizedOverlay#OnFocusChangeListener} will be notified. This does not
|
||||
* move the map, so if the Item isn't already centered, the user may get
|
||||
* confused. If the Item is not found, this is a no-op. You can also pass
|
||||
* null to remove focus.
|
||||
* @param item
|
||||
* ...
|
||||
*/
|
||||
public void setFocus(final Item item) {
|
||||
mFocusedItem = item;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the currently-focused item, or null if no item is currently
|
||||
* focused.
|
||||
*/
|
||||
public Item getFocus() {
|
||||
return mFocusedItem;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adjusts a drawable's bounds so that (0,0) is a pixel in the location
|
||||
* described by the hotspot parameter. Useful for "pin"-like graphics. For
|
||||
* convenience, returns the same drawable that was passed in.
|
||||
* @param marker
|
||||
* the drawable to adjust
|
||||
* @param hotspot
|
||||
* the hotspot for the drawable
|
||||
* @return the same drawable that was passed in.
|
||||
*/
|
||||
protected synchronized Drawable boundToHotspot(final Drawable marker, HotspotPlace hotspot) {
|
||||
final int markerWidth = marker.getIntrinsicWidth();
|
||||
final int markerHeight = marker.getIntrinsicHeight();
|
||||
|
||||
mRect.set(0, 0, 0 + markerWidth, 0 + markerHeight);
|
||||
|
||||
if (hotspot == null) {
|
||||
hotspot = HotspotPlace.BOTTOM_CENTER;
|
||||
}
|
||||
|
||||
switch (hotspot) {
|
||||
default:
|
||||
case NONE:
|
||||
break;
|
||||
case CENTER:
|
||||
mRect.offset(-markerWidth / 2, -markerHeight / 2);
|
||||
break;
|
||||
case BOTTOM_CENTER:
|
||||
mRect.offset(-markerWidth / 2, -markerHeight);
|
||||
break;
|
||||
case TOP_CENTER:
|
||||
mRect.offset(-markerWidth / 2, 0);
|
||||
break;
|
||||
case RIGHT_CENTER:
|
||||
mRect.offset(-markerWidth, -markerHeight / 2);
|
||||
break;
|
||||
case LEFT_CENTER:
|
||||
mRect.offset(0, -markerHeight / 2);
|
||||
break;
|
||||
case UPPER_RIGHT_CORNER:
|
||||
mRect.offset(-markerWidth, 0);
|
||||
break;
|
||||
case LOWER_RIGHT_CORNER:
|
||||
mRect.offset(-markerWidth, -markerHeight);
|
||||
break;
|
||||
case UPPER_LEFT_CORNER:
|
||||
mRect.offset(0, 0);
|
||||
break;
|
||||
case LOWER_LEFT_CORNER:
|
||||
mRect.offset(0, -markerHeight);
|
||||
break;
|
||||
}
|
||||
marker.setBounds(mRect);
|
||||
return marker;
|
||||
}
|
||||
|
||||
// /**
|
||||
// * Draw a marker on each of our items. populate() must have been called
|
||||
// * first.<br/>
|
||||
// * <br/>
|
||||
// * The marker will be drawn twice for each Item in the Overlay--once in the
|
||||
// * shadow phase, skewed and darkened, then again in the non-shadow phase.
|
||||
// * The bottom-center of the marker will be aligned with the geographical
|
||||
// * coordinates of the Item.<br/>
|
||||
// * <br/>
|
||||
// * The order of drawing may be changed by overriding the getIndexToDraw(int)
|
||||
// * method. An item may provide an alternate marker via its
|
||||
// * OverlayItem.getMarker(int) method. If that method returns null, the
|
||||
// * default marker is used.<br/>
|
||||
// * <br/>
|
||||
// * The focused item is always drawn last, which puts it visually on top of
|
||||
// * the other items.<br/>
|
||||
// *
|
||||
// * @param canvas
|
||||
// * the Canvas upon which to draw. Note that this may already have
|
||||
// * a transformation applied, so be sure to leave it the way you
|
||||
// * found it
|
||||
// * @param mapView
|
||||
// * the MapView that requested the draw. Use
|
||||
// * MapView.getProjection() to convert between on-screen pixels
|
||||
// * and latitude/longitude pairs
|
||||
// * @param shadow
|
||||
// * if true, draw the shadow layer. If false, draw the overlay
|
||||
// * contents.
|
||||
// */
|
||||
// @Override
|
||||
// public void draw(final Canvas canvas, final MapView mapView, final boolean shadow) {
|
||||
//
|
||||
// if (shadow) {
|
||||
// return;
|
||||
// }
|
||||
//
|
||||
// final Projection pj = mapView.getProjection();
|
||||
// final int size = this.mInternalItemList.size() - 1;
|
||||
//
|
||||
// /*
|
||||
// * Draw in backward cycle, so the items with the least index are on the
|
||||
// * front.
|
||||
// */
|
||||
// for (int i = size; i >= 0; i--) {
|
||||
// final Item item = getItem(i);
|
||||
// pj.toMapPixels(item.mGeoPoint, mCurScreenCoords);
|
||||
//
|
||||
// onDrawItem(canvas, item, mCurScreenCoords);
|
||||
// }
|
||||
// }
|
||||
|
||||
// /**
|
||||
// * Draws an item located at the provided screen coordinates to the canvas.
|
||||
// *
|
||||
// * @param canvas
|
||||
// * what the item is drawn upon
|
||||
// * @param item
|
||||
// * the item to be drawn
|
||||
// * @param curScreenCoords
|
||||
// * the screen coordinates of the item
|
||||
// */
|
||||
// protected void onDrawItem(final Canvas canvas, final Item item, final Point curScreenCoords) {
|
||||
// int state = 0;
|
||||
//
|
||||
// if (mDrawFocusedItem && (mFocusedItem == item))
|
||||
// state = OverlayItem.ITEM_STATE_FOCUSED_MASK;
|
||||
//
|
||||
// Drawable marker;
|
||||
//
|
||||
// if (item.getMarker(state) == null)
|
||||
// marker = getDefaultMarker(state);
|
||||
// else
|
||||
// marker = item.getMarker(state);
|
||||
//
|
||||
// boundToHotspot(marker, item.getMarkerHotspot());
|
||||
//
|
||||
// // draw it
|
||||
// Overlay.drawAt(canvas, marker, curScreenCoords.x, curScreenCoords.y, false);
|
||||
// }
|
||||
}
|
||||
33
src/org/oscim/overlay/LabelingOverlay.java
Normal file
@ -0,0 +1,33 @@
|
||||
/*
|
||||
* Copyright 2012 Hannes Janetzek
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it under the
|
||||
* terms of the GNU Lesser 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 Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.oscim.overlay;
|
||||
|
||||
import org.oscim.renderer.overlays.OverlayText;
|
||||
import org.oscim.view.MapView;
|
||||
|
||||
public class LabelingOverlay extends Overlay {
|
||||
|
||||
// private OverlayText mLayer;
|
||||
|
||||
// @Override
|
||||
// public org.oscim.renderer.overlays.RenderOverlay getLayer() {
|
||||
// return mLayer;
|
||||
// }
|
||||
|
||||
public LabelingOverlay(MapView mapView) {
|
||||
super();
|
||||
mLayer = new OverlayText(mapView);
|
||||
}
|
||||
}
|
||||
36
src/org/oscim/overlay/MapViewConstants.java
Normal file
@ -0,0 +1,36 @@
|
||||
// Created by plusminus on 18:00:24 - 25.09.2008
|
||||
package org.oscim.overlay;
|
||||
|
||||
/**
|
||||
*
|
||||
* This class contains constants used by the map view.
|
||||
*
|
||||
* @author Nicolas Gramlich
|
||||
*
|
||||
*/
|
||||
public interface MapViewConstants {
|
||||
// ===========================================================
|
||||
// Final Fields
|
||||
// ===========================================================
|
||||
|
||||
public static final boolean DEBUGMODE = false;
|
||||
|
||||
public static final int NOT_SET = Integer.MIN_VALUE;
|
||||
|
||||
public static final int ANIMATION_SMOOTHNESS_LOW = 4;
|
||||
public static final int ANIMATION_SMOOTHNESS_DEFAULT = 10;
|
||||
public static final int ANIMATION_SMOOTHNESS_HIGH = 20;
|
||||
|
||||
public static final int ANIMATION_DURATION_SHORT = 500;
|
||||
public static final int ANIMATION_DURATION_DEFAULT = 1000;
|
||||
public static final int ANIMATION_DURATION_LONG = 2000;
|
||||
|
||||
/** Minimum Zoom Level */
|
||||
public static final int MINIMUM_ZOOMLEVEL = 0;
|
||||
|
||||
/**
|
||||
* Maximum Zoom Level - we use Integers to store zoom levels so overflow happens at 2^32 - 1,
|
||||
* but we also have a tile size that is typically 2^8, so (32-1)-8-1 = 22
|
||||
*/
|
||||
public static final int MAXIMUM_ZOOMLEVEL = 22;
|
||||
}
|
||||
450
src/org/oscim/overlay/Overlay.java
Normal file
@ -0,0 +1,450 @@
|
||||
// Created by plusminus on 20:32:01 - 27.09.2008
|
||||
package org.oscim.overlay;
|
||||
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import org.oscim.core.MapPosition;
|
||||
import org.oscim.renderer.overlays.RenderOverlay;
|
||||
import org.oscim.view.MapView;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Point;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.MotionEvent;
|
||||
|
||||
/**
|
||||
* Base class representing an overlay which may be displayed on top of a
|
||||
* {@link MapView}. To add an overlay, subclass this class, create an instance,
|
||||
* and add it to the list obtained from getOverlays() of {@link MapView}. This
|
||||
* class implements a form of Gesture Handling similar to
|
||||
* {@link android.view.GestureDetector.SimpleOnGestureListener} and
|
||||
* GestureDetector.OnGestureListener. The difference is there is an additional
|
||||
* argument for the item.
|
||||
*
|
||||
* @author Nicolas Gramlich
|
||||
*/
|
||||
public abstract class Overlay implements OverlayConstants {
|
||||
|
||||
// ===========================================================
|
||||
// Constants
|
||||
// ===========================================================
|
||||
|
||||
private static AtomicInteger sOrdinal = new AtomicInteger();
|
||||
|
||||
// From Google Maps API
|
||||
protected static final float SHADOW_X_SKEW = -0.8999999761581421f;
|
||||
protected static final float SHADOW_Y_SCALE = 0.5f;
|
||||
|
||||
// ===========================================================
|
||||
// Fields
|
||||
// ===========================================================
|
||||
|
||||
protected final ResourceProxy mResourceProxy;
|
||||
protected final float mScale;
|
||||
|
||||
// private static final Rect mRect = new Rect();
|
||||
private boolean mEnabled = true;
|
||||
|
||||
protected RenderOverlay mLayer;
|
||||
|
||||
public RenderOverlay getLayer() {
|
||||
return mLayer;
|
||||
}
|
||||
|
||||
// ===========================================================
|
||||
// Constructors
|
||||
// ===========================================================
|
||||
|
||||
public Overlay() {
|
||||
mResourceProxy = null;
|
||||
mScale = 1;
|
||||
// mResourceProxy = new DefaultResourceProxyImpl(ctx);
|
||||
// mScale = ctx.getResources().getDisplayMetrics().density;
|
||||
}
|
||||
|
||||
public Overlay(final Context ctx) {
|
||||
mResourceProxy = new DefaultResourceProxyImpl(ctx);
|
||||
mScale = ctx.getResources().getDisplayMetrics().density;
|
||||
}
|
||||
|
||||
public Overlay(final ResourceProxy pResourceProxy) {
|
||||
mResourceProxy = pResourceProxy;
|
||||
mScale = mResourceProxy.getDisplayMetricsDensity();
|
||||
}
|
||||
|
||||
// ===========================================================
|
||||
// Getter & Setter
|
||||
// ===========================================================
|
||||
|
||||
/**
|
||||
* Sets whether the Overlay is marked to be enabled. This setting does
|
||||
* nothing by default, but should be checked before calling draw().
|
||||
*
|
||||
* @param pEnabled
|
||||
* ...
|
||||
*/
|
||||
public void setEnabled(final boolean pEnabled) {
|
||||
this.mEnabled = pEnabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specifies if the Overlay is marked to be enabled. This should be checked
|
||||
* before calling draw().
|
||||
*
|
||||
* @return true if the Overlay is marked enabled, false otherwise
|
||||
*/
|
||||
public boolean isEnabled() {
|
||||
return this.mEnabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* Since the menu-chain will pass through several independent Overlays, menu
|
||||
* IDs cannot be fixed at compile time. Overlays should use this method to
|
||||
* obtain and store a menu id for each menu item at construction time. This
|
||||
* will ensure that two overlays don't use the same id.
|
||||
*
|
||||
* @return an integer suitable to be used as a menu identifier
|
||||
*/
|
||||
protected final static int getSafeMenuId() {
|
||||
return sOrdinal.getAndIncrement();
|
||||
}
|
||||
|
||||
/**
|
||||
* Similar to <see cref="getSafeMenuId" />, except this reserves a sequence
|
||||
* of IDs of length <param name="count" />. The returned number is the
|
||||
* starting index of that sequential list.
|
||||
*
|
||||
* @param count
|
||||
* ....
|
||||
* @return an integer suitable to be used as a menu identifier
|
||||
*/
|
||||
protected final static int getSafeMenuIdSequence(final int count) {
|
||||
return sOrdinal.getAndAdd(count);
|
||||
}
|
||||
|
||||
// ===========================================================
|
||||
// Methods for SuperClass/Interfaces
|
||||
// ===========================================================
|
||||
|
||||
// /**
|
||||
// * Draw the overlay over the map. This will be called on all active overlays
|
||||
// * with shadow=true, to lay down the shadow layer, and then again on all
|
||||
// * overlays with shadow=false. Callers should check isEnabled() before
|
||||
// * calling draw(). By default, draws nothing.
|
||||
// *
|
||||
// * @param c
|
||||
// * ...
|
||||
// * @param osmv
|
||||
// * ...
|
||||
// * @param shadow
|
||||
// * ...
|
||||
// */
|
||||
// protected abstract void draw(final Canvas c, final MapView osmv, final boolean shadow);
|
||||
|
||||
// ===========================================================
|
||||
// Methods
|
||||
// ===========================================================
|
||||
|
||||
/**
|
||||
* Override to perform clean up of resources before shutdown. By default
|
||||
* does nothing.
|
||||
*
|
||||
* @param mapView
|
||||
* ...
|
||||
*/
|
||||
public void onDetach(final MapView mapView) {
|
||||
}
|
||||
|
||||
/**
|
||||
* By default does nothing (<code>return false</code>). If you handled the
|
||||
* Event, return <code>true</code>, otherwise return <code>false</code>. If
|
||||
* you returned <code>true</code> none of the following Overlays or the
|
||||
* underlying {@link MapView} has the chance to handle this event.
|
||||
*
|
||||
* @param keyCode
|
||||
* ...
|
||||
* @param event
|
||||
* ...
|
||||
* @param mapView
|
||||
* ...
|
||||
* @return ...
|
||||
*/
|
||||
public boolean onKeyDown(final int keyCode, final KeyEvent event, final MapView mapView) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* By default does nothing (<code>return false</code>). If you handled the
|
||||
* Event, return <code>true</code>, otherwise return <code>false</code>. If
|
||||
* you returned <code>true</code> none of the following Overlays or the
|
||||
* underlying {@link MapView} has the chance to handle this event.
|
||||
*
|
||||
* @param keyCode
|
||||
* ...
|
||||
* @param event
|
||||
* ...
|
||||
* @param mapView
|
||||
* ...
|
||||
* @return ...
|
||||
*/
|
||||
public boolean onKeyUp(final int keyCode, final KeyEvent event, final MapView mapView) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* <b>You can prevent all(!) other Touch-related events from happening!</b><br />
|
||||
* By default does nothing (<code>return false</code>). If you handled the
|
||||
* Event, return <code>true</code>, otherwise return <code>false</code>. If
|
||||
* you returned <code>true</code> none of the following Overlays or the
|
||||
* underlying {@link MapView} has the chance to handle this event.
|
||||
*
|
||||
* @param e
|
||||
* ...
|
||||
* @param mapView
|
||||
* ...
|
||||
* @return ...
|
||||
*/
|
||||
public boolean onTouchEvent(final MotionEvent e, final MapView mapView) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* By default does nothing (<code>return false</code>). If you handled the
|
||||
* Event, return <code>true</code>, otherwise return <code>false</code>. If
|
||||
* you returned <code>true</code> none of the following Overlays or the
|
||||
* underlying {@link MapView} has the chance to handle this event.
|
||||
*
|
||||
* @param e
|
||||
* ...
|
||||
* @param mapView
|
||||
* ...
|
||||
* @return ...
|
||||
*/
|
||||
public boolean onTrackballEvent(final MotionEvent e, final MapView mapView) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/** GestureDetector.OnDoubleTapListener **/
|
||||
|
||||
/**
|
||||
* By default does nothing (<code>return false</code>). If you handled the
|
||||
* Event, return <code>true</code>, otherwise return <code>false</code>. If
|
||||
* you returned <code>true</code> none of the following Overlays or the
|
||||
* underlying {@link MapView} has the chance to handle this event.
|
||||
*
|
||||
* @param e
|
||||
* ...
|
||||
* @param mapView
|
||||
* ...
|
||||
* @return ...
|
||||
*/
|
||||
public boolean onDoubleTap(final MotionEvent e, final MapView mapView) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* By default does nothing (<code>return false</code>). If you handled the
|
||||
* Event, return <code>true</code>, otherwise return <code>false</code>. If
|
||||
* you returned <code>true</code> none of the following Overlays or the
|
||||
* underlying {@link MapView} has the chance to handle this event.
|
||||
*
|
||||
* @param e
|
||||
* ...
|
||||
* @param mapView
|
||||
* ...
|
||||
* @return ...
|
||||
*/
|
||||
public boolean onDoubleTapEvent(final MotionEvent e, final MapView mapView) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* By default does nothing (<code>return false</code>). If you handled the
|
||||
* Event, return <code>true</code>, otherwise return <code>false</code>. If
|
||||
* you returned <code>true</code> none of the following Overlays or the
|
||||
* underlying {@link MapView} has the chance to handle this event.
|
||||
*
|
||||
* @param e
|
||||
* ...
|
||||
* @param mapView
|
||||
* ...
|
||||
* @return ...
|
||||
*/
|
||||
public boolean onSingleTapConfirmed(final MotionEvent e, final MapView mapView) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/** OnGestureListener **/
|
||||
|
||||
/**
|
||||
* By default does nothing (<code>return false</code>). If you handled the
|
||||
* Event, return <code>true</code>, otherwise return <code>false</code>. If
|
||||
* you returned <code>true</code> none of the following Overlays or the
|
||||
* underlying {@link MapView} has the chance to handle this event.
|
||||
*
|
||||
* @param e
|
||||
* ...
|
||||
* @param mapView
|
||||
* ...
|
||||
* @return ...
|
||||
*/
|
||||
public boolean onDown(final MotionEvent e, final MapView mapView) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* By default does nothing (<code>return false</code>). If you handled the
|
||||
* Event, return <code>true</code>, otherwise return <code>false</code>. If
|
||||
* you returned <code>true</code> none of the following Overlays or the
|
||||
* underlying {@link MapView} has the chance to handle this event.
|
||||
*
|
||||
* @param pEvent1
|
||||
* ...
|
||||
* @param pEvent2
|
||||
* ...
|
||||
* @param pVelocityX
|
||||
* ...
|
||||
* @param pVelocityY
|
||||
* ...
|
||||
* @param pMapView
|
||||
* ...
|
||||
* @return ...
|
||||
*/
|
||||
public boolean onFling(final MotionEvent pEvent1, final MotionEvent pEvent2,
|
||||
final float pVelocityX, final float pVelocityY, final MapView pMapView) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* By default does nothing (<code>return false</code>). If you handled the
|
||||
* Event, return <code>true</code>, otherwise return <code>false</code>. If
|
||||
* you returned <code>true</code> none of the following Overlays or the
|
||||
* underlying {@link MapView} has the chance to handle this event.
|
||||
*
|
||||
* @param e
|
||||
* ...
|
||||
* @param mapView
|
||||
* ...
|
||||
* @return ...
|
||||
*/
|
||||
public boolean onLongPress(final MotionEvent e, final MapView mapView) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* By default does nothing (<code>return false</code>). If you handled the
|
||||
* Event, return <code>true</code>, otherwise return <code>false</code>. If
|
||||
* you returned <code>true</code> none of the following Overlays or the
|
||||
* underlying {@link MapView} has the chance to handle this event.
|
||||
*
|
||||
* @param pEvent1
|
||||
* ...
|
||||
* @param pEvent2
|
||||
* ...
|
||||
* @param pDistanceX
|
||||
* ...
|
||||
* @param pDistanceY
|
||||
* ...
|
||||
* @param pMapView
|
||||
* ...
|
||||
* @return ...
|
||||
*/
|
||||
public boolean onScroll(final MotionEvent pEvent1, final MotionEvent pEvent2,
|
||||
final float pDistanceX, final float pDistanceY, final MapView pMapView) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param pEvent
|
||||
* ...
|
||||
* @param pMapView
|
||||
* ...
|
||||
*/
|
||||
public void onShowPress(final MotionEvent pEvent, final MapView pMapView) {
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* By default does nothing (<code>return false</code>). If you handled the
|
||||
* Event, return <code>true</code>, otherwise return <code>false</code>. If
|
||||
* you returned <code>true</code> none of the following Overlays or the
|
||||
* underlying {@link MapView} has the chance to handle this event.
|
||||
*
|
||||
* @param e
|
||||
* ...
|
||||
* @param mapView
|
||||
* ...
|
||||
* @return ...
|
||||
*/
|
||||
public boolean onSingleTapUp(final MotionEvent e, final MapView mapView) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mapPosition
|
||||
* current MapPosition
|
||||
*/
|
||||
public void onUpdate(MapPosition mapPosition) {
|
||||
|
||||
}
|
||||
|
||||
// /**
|
||||
// * Convenience method to draw a Drawable at an offset. x and y are pixel
|
||||
// * coordinates. You can find appropriate coordinates from latitude/longitude
|
||||
// * using the MapView.getProjection() method on the MapView passed to you in
|
||||
// * draw(Canvas, MapView, boolean).
|
||||
// *
|
||||
// * @param canvas
|
||||
// * ...
|
||||
// * @param drawable
|
||||
// * ...
|
||||
// * @param x
|
||||
// * ...
|
||||
// * @param y
|
||||
// * ...
|
||||
// * @param shadow
|
||||
// * If true, draw only the drawable's shadow. Otherwise, draw the
|
||||
// * drawable itself.
|
||||
// */
|
||||
// protected synchronized static void drawAt(final android.graphics.Canvas canvas,
|
||||
// final android.graphics.drawable.Drawable drawable, final int x, final int y,
|
||||
// final boolean shadow) {
|
||||
// drawable.copyBounds(mRect);
|
||||
// drawable.setBounds(mRect.left + x, mRect.top + y, mRect.right + x, mRect.bottom + y);
|
||||
// drawable.draw(canvas);
|
||||
// drawable.setBounds(mRect);
|
||||
// }
|
||||
|
||||
// ===========================================================
|
||||
// Inner and Anonymous Classes
|
||||
// ===========================================================
|
||||
|
||||
/**
|
||||
* Interface definition for overlays that contain items that can be snapped
|
||||
* to (for example, when the user invokes a zoom, this could be called
|
||||
* allowing the user to snap the zoom to an interesting point.)
|
||||
*/
|
||||
public interface Snappable {
|
||||
|
||||
/**
|
||||
* Checks to see if the given x and y are close enough to an item
|
||||
* resulting in snapping the current action (e.g. zoom) to the item.
|
||||
*
|
||||
* @param x
|
||||
* The x in screen coordinates.
|
||||
* @param y
|
||||
* The y in screen coordinates.
|
||||
* @param snapPoint
|
||||
* To be filled with the the interesting point (in screen
|
||||
* coordinates) that is closest to the given x and y. Can be
|
||||
* untouched if not snapping.
|
||||
* @param mapView
|
||||
* The {@link MapView} that is requesting the snap. Use
|
||||
* MapView.getProjection() to convert between on-screen
|
||||
* pixels and latitude/longitude pairs.
|
||||
* @return Whether or not to snap to the interesting point.
|
||||
*/
|
||||
boolean onSnapToItem(int x, int y, Point snapPoint, MapView mapView);
|
||||
}
|
||||
|
||||
}
|
||||
16
src/org/oscim/overlay/OverlayConstants.java
Normal file
@ -0,0 +1,16 @@
|
||||
package org.oscim.overlay;
|
||||
|
||||
/**
|
||||
* This class contains constants used by the overlays.
|
||||
*/
|
||||
public interface OverlayConstants {
|
||||
// ===========================================================
|
||||
// Final Fields
|
||||
// ===========================================================
|
||||
|
||||
public static final boolean DEBUGMODE = false;
|
||||
|
||||
public static final int NOT_SET = Integer.MIN_VALUE;
|
||||
|
||||
public static final int DEFAULT_ZOOMLEVEL_MINIMAP_DIFFERENCE = 3;
|
||||
}
|
||||
157
src/org/oscim/overlay/OverlayItem.java
Normal file
@ -0,0 +1,157 @@
|
||||
// Created by plusminus on 00:02:58 - 03.10.2008
|
||||
package org.oscim.overlay;
|
||||
|
||||
import org.oscim.core.GeoPoint;
|
||||
|
||||
import android.graphics.Point;
|
||||
import android.graphics.drawable.Drawable;
|
||||
|
||||
/** Immutable class describing a GeoPoint with a Title and a Description.
|
||||
* @author Nicolas Gramlich
|
||||
* @author Theodore Hong
|
||||
* @author Fred Eisele */
|
||||
public class OverlayItem {
|
||||
|
||||
// ===========================================================
|
||||
// Constants
|
||||
// ===========================================================
|
||||
public static final int ITEM_STATE_FOCUSED_MASK = 4;
|
||||
public static final int ITEM_STATE_PRESSED_MASK = 1;
|
||||
public static final int ITEM_STATE_SELECTED_MASK = 2;
|
||||
|
||||
protected static final Point DEFAULT_MARKER_SIZE = new Point(26, 94);
|
||||
|
||||
/** Indicates a hotspot for an area. This is where the origin (0,0) of a
|
||||
* point will be located relative to the area. In otherwords this acts as an
|
||||
* offset. NONE indicates that no adjustment should be made. */
|
||||
public enum HotspotPlace {
|
||||
NONE, CENTER, BOTTOM_CENTER, TOP_CENTER, RIGHT_CENTER, LEFT_CENTER, UPPER_RIGHT_CORNER, LOWER_RIGHT_CORNER, UPPER_LEFT_CORNER, LOWER_LEFT_CORNER
|
||||
}
|
||||
|
||||
// ===========================================================
|
||||
// Fields
|
||||
// ===========================================================
|
||||
|
||||
public final String mUid;
|
||||
public final String mTitle;
|
||||
public final String mDescription;
|
||||
public final GeoPoint mGeoPoint;
|
||||
protected Drawable mMarker;
|
||||
protected HotspotPlace mHotspotPlace;
|
||||
|
||||
// ===========================================================
|
||||
// Constructors
|
||||
// ===========================================================
|
||||
|
||||
/** @param aTitle
|
||||
* this should be <b>singleLine</b> (no <code>'\n'</code> )
|
||||
* @param aDescription
|
||||
* a <b>multiLine</b> description ( <code>'\n'</code> possible)
|
||||
* @param aGeoPoint
|
||||
* ... */
|
||||
public OverlayItem(final String aTitle, final String aDescription, final GeoPoint aGeoPoint) {
|
||||
this(null, aTitle, aDescription, aGeoPoint);
|
||||
}
|
||||
|
||||
public OverlayItem(final String aUid, final String aTitle, final String aDescription,
|
||||
final GeoPoint aGeoPoint) {
|
||||
this.mTitle = aTitle;
|
||||
this.mDescription = aDescription;
|
||||
this.mGeoPoint = aGeoPoint;
|
||||
this.mUid = aUid;
|
||||
}
|
||||
|
||||
// ===========================================================
|
||||
// Getter & Setter
|
||||
// ===========================================================
|
||||
public String getUid() {
|
||||
return mUid;
|
||||
}
|
||||
|
||||
public String getTitle() {
|
||||
return mTitle;
|
||||
}
|
||||
|
||||
public String getSnippet() {
|
||||
return mDescription;
|
||||
}
|
||||
|
||||
public GeoPoint getPoint() {
|
||||
return mGeoPoint;
|
||||
}
|
||||
|
||||
/* (copied from Google API docs) Returns the marker that should be used when
|
||||
* drawing this item on the map. A null value means that the default marker
|
||||
* should be drawn. Different markers can be returned for different states.
|
||||
* The different markers can have different bounds. The default behavior is
|
||||
* to call {@link setState(android.graphics.drawable.Drawable, int)} on the
|
||||
* overlay item's marker, if it exists, and then return it.
|
||||
* @param stateBitset The current state.
|
||||
* @return The marker for the current state, or null if the default marker
|
||||
* for the overlay should be used. */
|
||||
public Drawable getMarker(final int stateBitset) {
|
||||
// marker not specified
|
||||
if (mMarker == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// set marker state appropriately
|
||||
setState(mMarker, stateBitset);
|
||||
return mMarker;
|
||||
}
|
||||
|
||||
public void setMarker(final Drawable marker) {
|
||||
this.mMarker = marker;
|
||||
}
|
||||
|
||||
public void setMarkerHotspot(final HotspotPlace place) {
|
||||
this.mHotspotPlace = (place == null) ? HotspotPlace.BOTTOM_CENTER : place;
|
||||
}
|
||||
|
||||
public HotspotPlace getMarkerHotspot() {
|
||||
return this.mHotspotPlace;
|
||||
}
|
||||
|
||||
// ===========================================================
|
||||
// Methods from SuperClass/Interfaces
|
||||
// ===========================================================
|
||||
|
||||
// ===========================================================
|
||||
// Methods
|
||||
// ===========================================================
|
||||
/* (copied from the Google API docs) Sets the state of a drawable to match a
|
||||
* given state bitset. This is done by converting the state bitset bits
|
||||
* into
|
||||
* a state set of R.attr.state_pressed, R.attr.state_selected and
|
||||
* R.attr.state_focused attributes, and then calling {@link
|
||||
* Drawable.setState(int[])}. */
|
||||
public static void setState(final Drawable drawable, final int stateBitset) {
|
||||
final int[] states = new int[3];
|
||||
int index = 0;
|
||||
if ((stateBitset & ITEM_STATE_PRESSED_MASK) > 0)
|
||||
states[index++] = android.R.attr.state_pressed;
|
||||
if ((stateBitset & ITEM_STATE_SELECTED_MASK) > 0)
|
||||
states[index++] = android.R.attr.state_selected;
|
||||
if ((stateBitset & ITEM_STATE_FOCUSED_MASK) > 0)
|
||||
states[index++] = android.R.attr.state_focused;
|
||||
|
||||
drawable.setState(states);
|
||||
}
|
||||
|
||||
public Drawable getDrawable() {
|
||||
return this.mMarker;
|
||||
}
|
||||
|
||||
public int getWidth() {
|
||||
return this.mMarker.getIntrinsicWidth();
|
||||
}
|
||||
|
||||
public int getHeight() {
|
||||
return this.mMarker.getIntrinsicHeight();
|
||||
}
|
||||
|
||||
// ===========================================================
|
||||
// Inner and Anonymous Classes
|
||||
// ===========================================================
|
||||
|
||||
}
|
||||
390
src/org/oscim/overlay/OverlayManager.java
Normal file
@ -0,0 +1,390 @@
|
||||
package org.oscim.overlay;
|
||||
|
||||
import java.util.AbstractList;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.ListIterator;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
|
||||
import org.oscim.core.MapPosition;
|
||||
import org.oscim.overlay.Overlay.Snappable;
|
||||
import org.oscim.renderer.overlays.RenderOverlay;
|
||||
import org.oscim.view.MapView;
|
||||
|
||||
import android.graphics.Point;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.MotionEvent;
|
||||
|
||||
public class OverlayManager extends AbstractList<Overlay> {
|
||||
|
||||
// private TilesOverlay mTilesOverlay;
|
||||
|
||||
/* package */final CopyOnWriteArrayList<Overlay> mOverlayList;
|
||||
|
||||
public OverlayManager() {
|
||||
// final TilesOverlay tilesOverlay) {
|
||||
// setTilesOverlay(tilesOverlay);
|
||||
mOverlayList = new CopyOnWriteArrayList<Overlay>();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Overlay get(final int pIndex) {
|
||||
return mOverlayList.get(pIndex);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
return mOverlayList.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void add(final int pIndex, final Overlay pElement) {
|
||||
mOverlayList.add(pIndex, pElement);
|
||||
mUpdateLayers = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Overlay remove(final int pIndex) {
|
||||
mUpdateLayers = true;
|
||||
return mOverlayList.remove(pIndex);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Overlay set(final int pIndex, final Overlay pElement) {
|
||||
mUpdateLayers = true;
|
||||
return mOverlayList.set(pIndex, pElement);
|
||||
}
|
||||
|
||||
// /**
|
||||
// * Gets the optional TilesOverlay class.
|
||||
// *
|
||||
// * @return the tilesOverlay
|
||||
// */
|
||||
// public TilesOverlay getTilesOverlay() {
|
||||
// return mTilesOverlay;
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * Sets the optional TilesOverlay class. If set, this overlay will be
|
||||
// drawn before all other
|
||||
// * overlays and will not be included in the editable list of overlays and
|
||||
// can't be cleared
|
||||
// * except by a subsequent call to setTilesOverlay().
|
||||
// *
|
||||
// * @param tilesOverlay
|
||||
// * the tilesOverlay to set
|
||||
// */
|
||||
// public void setTilesOverlay(final TilesOverlay tilesOverlay) {
|
||||
// mTilesOverlay = tilesOverlay;
|
||||
// }
|
||||
|
||||
public Iterable<Overlay> overlaysReversed() {
|
||||
return new Iterable<Overlay>() {
|
||||
@Override
|
||||
public Iterator<Overlay> iterator() {
|
||||
final ListIterator<Overlay> i = mOverlayList.listIterator(mOverlayList.size());
|
||||
|
||||
return new Iterator<Overlay>() {
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
return i.hasPrevious();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Overlay next() {
|
||||
return i.previous();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove() {
|
||||
i.remove();
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private boolean mUpdateLayers;
|
||||
private List<RenderOverlay> mDrawLayers = new ArrayList<RenderOverlay>();
|
||||
|
||||
public List<RenderOverlay> getRenderLayers() {
|
||||
if (mUpdateLayers) {
|
||||
synchronized (this) {
|
||||
|
||||
mUpdateLayers = false;
|
||||
mDrawLayers.clear();
|
||||
|
||||
for (Overlay o : mOverlayList) {
|
||||
RenderOverlay l = o.getLayer();
|
||||
if (l != null)
|
||||
mDrawLayers.add(l);
|
||||
}
|
||||
}
|
||||
}
|
||||
return mDrawLayers;
|
||||
}
|
||||
|
||||
// public void onDraw(final Canvas c, final MapView pMapView) {
|
||||
// // if ((mTilesOverlay != null) && mTilesOverlay.isEnabled()) {
|
||||
// // mTilesOverlay.draw(c, pMapView, true);
|
||||
// // }
|
||||
// //
|
||||
// // if ((mTilesOverlay != null) && mTilesOverlay.isEnabled()) {
|
||||
// // mTilesOverlay.draw(c, pMapView, false);
|
||||
// // }
|
||||
//
|
||||
// for (final Overlay overlay : mOverlayList) {
|
||||
// if (overlay.isEnabled()) {
|
||||
// overlay.draw(c, pMapView, true);
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// for (final Overlay overlay : mOverlayList) {
|
||||
// if (overlay.isEnabled()) {
|
||||
// overlay.draw(c, pMapView, false);
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// }
|
||||
|
||||
public void onDetach(final MapView pMapView) {
|
||||
// if (mTilesOverlay != null) {
|
||||
// mTilesOverlay.onDetach(pMapView);
|
||||
// }
|
||||
|
||||
for (final Overlay overlay : this.overlaysReversed()) {
|
||||
overlay.onDetach(pMapView);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean onKeyDown(final int keyCode, final KeyEvent event, final MapView pMapView) {
|
||||
for (final Overlay overlay : this.overlaysReversed()) {
|
||||
if (overlay.onKeyDown(keyCode, event, pMapView)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean onKeyUp(final int keyCode, final KeyEvent event, final MapView pMapView) {
|
||||
for (final Overlay overlay : this.overlaysReversed()) {
|
||||
if (overlay.onKeyUp(keyCode, event, pMapView)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean onTouchEvent(final MotionEvent event, final MapView pMapView) {
|
||||
for (final Overlay overlay : this.overlaysReversed()) {
|
||||
if (overlay.onTouchEvent(event, pMapView)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean onTrackballEvent(final MotionEvent event, final MapView pMapView) {
|
||||
for (final Overlay overlay : this.overlaysReversed()) {
|
||||
if (overlay.onTrackballEvent(event, pMapView)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean onSnapToItem(final int x, final int y, final Point snapPoint,
|
||||
final MapView pMapView) {
|
||||
for (final Overlay overlay : this.overlaysReversed()) {
|
||||
if (overlay instanceof Snappable) {
|
||||
if (((Snappable) overlay).onSnapToItem(x, y, snapPoint, pMapView)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/* GestureDetector.OnDoubleTapListener */
|
||||
|
||||
public boolean onDoubleTap(final MotionEvent e, final MapView pMapView) {
|
||||
for (final Overlay overlay : this.overlaysReversed()) {
|
||||
if (overlay.onDoubleTap(e, pMapView)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean onDoubleTapEvent(final MotionEvent e, final MapView pMapView) {
|
||||
for (final Overlay overlay : this.overlaysReversed()) {
|
||||
if (overlay.onDoubleTapEvent(e, pMapView)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean onSingleTapConfirmed(final MotionEvent e, final MapView pMapView) {
|
||||
for (final Overlay overlay : this.overlaysReversed()) {
|
||||
if (overlay.onSingleTapConfirmed(e, pMapView)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/* OnGestureListener */
|
||||
|
||||
public boolean onDown(final MotionEvent pEvent, final MapView pMapView) {
|
||||
for (final Overlay overlay : this.overlaysReversed()) {
|
||||
if (overlay.onDown(pEvent, pMapView)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean onFling(final MotionEvent pEvent1, final MotionEvent pEvent2,
|
||||
final float pVelocityX, final float pVelocityY, final MapView pMapView) {
|
||||
for (final Overlay overlay : this.overlaysReversed()) {
|
||||
if (overlay.onFling(pEvent1, pEvent2, pVelocityX, pVelocityY, pMapView)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean onLongPress(final MotionEvent pEvent, final MapView pMapView) {
|
||||
for (final Overlay overlay : this.overlaysReversed()) {
|
||||
if (overlay.onLongPress(pEvent, pMapView)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean onScroll(final MotionEvent pEvent1, final MotionEvent pEvent2,
|
||||
final float pDistanceX, final float pDistanceY, final MapView pMapView) {
|
||||
for (final Overlay overlay : this.overlaysReversed()) {
|
||||
if (overlay.onScroll(pEvent1, pEvent2, pDistanceX, pDistanceY, pMapView)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public void onShowPress(final MotionEvent pEvent, final MapView pMapView) {
|
||||
for (final Overlay overlay : this.overlaysReversed()) {
|
||||
overlay.onShowPress(pEvent, pMapView);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean onSingleTapUp(final MotionEvent pEvent, final MapView pMapView) {
|
||||
for (final Overlay overlay : this.overlaysReversed()) {
|
||||
if (overlay.onSingleTapUp(pEvent, pMapView)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public void onUpdate(MapPosition mapPosition) {
|
||||
for (final Overlay overlay : this.overlaysReversed()) {
|
||||
overlay.onUpdate(mapPosition);
|
||||
}
|
||||
}
|
||||
|
||||
// ** Options Menu **//
|
||||
|
||||
// public void setOptionsMenusEnabled(final boolean pEnabled) {
|
||||
// for (final Overlay overlay : mOverlayList) {
|
||||
// if ((overlay instanceof IOverlayMenuProvider)
|
||||
// && ((IOverlayMenuProvider) overlay).isOptionsMenuEnabled()) {
|
||||
// ((IOverlayMenuProvider) overlay).setOptionsMenuEnabled(pEnabled);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// public boolean onCreateOptionsMenu(final Menu pMenu, final int
|
||||
// menuIdOffset,
|
||||
// final MapView mapView) {
|
||||
// boolean result = true;
|
||||
// for (final Overlay overlay : this.overlaysReversed()) {
|
||||
// if ((overlay instanceof IOverlayMenuProvider)
|
||||
// && ((IOverlayMenuProvider) overlay).isOptionsMenuEnabled()) {
|
||||
// result &= ((IOverlayMenuProvider) overlay).onCreateOptionsMenu(pMenu,
|
||||
// menuIdOffset,
|
||||
// mapView);
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// if ((mTilesOverlay != null) && (mTilesOverlay instanceof
|
||||
// IOverlayMenuProvider)
|
||||
// && ((IOverlayMenuProvider) mTilesOverlay).isOptionsMenuEnabled()) {
|
||||
// result &= mTilesOverlay.onCreateOptionsMenu(pMenu, menuIdOffset,
|
||||
// mapView);
|
||||
// }
|
||||
//
|
||||
// return result;
|
||||
// }
|
||||
//
|
||||
// public boolean onPrepareOptionsMenu(final Menu pMenu, final int
|
||||
// menuIdOffset,
|
||||
// final MapView mapView) {
|
||||
// for (final Overlay overlay : this.overlaysReversed()) {
|
||||
// if ((overlay instanceof IOverlayMenuProvider)
|
||||
// && ((IOverlayMenuProvider) overlay).isOptionsMenuEnabled()) {
|
||||
// ((IOverlayMenuProvider) overlay).onPrepareOptionsMenu(pMenu,
|
||||
// menuIdOffset, mapView);
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// if ((mTilesOverlay != null) && (mTilesOverlay instanceof
|
||||
// IOverlayMenuProvider)
|
||||
// && ((IOverlayMenuProvider) mTilesOverlay).isOptionsMenuEnabled()) {
|
||||
// mTilesOverlay.onPrepareOptionsMenu(pMenu, menuIdOffset, mapView);
|
||||
// }
|
||||
//
|
||||
// return true;
|
||||
// }
|
||||
//
|
||||
// public boolean onOptionsItemSelected(final MenuItem item, final int
|
||||
// menuIdOffset,
|
||||
// final MapView mapView) {
|
||||
// for (final Overlay overlay : this.overlaysReversed()) {
|
||||
// if ((overlay instanceof IOverlayMenuProvider)
|
||||
// && ((IOverlayMenuProvider) overlay).isOptionsMenuEnabled()
|
||||
// && ((IOverlayMenuProvider) overlay).onOptionsItemSelected(item,
|
||||
// menuIdOffset,
|
||||
// mapView)) {
|
||||
// return true;
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// if ((mTilesOverlay != null)
|
||||
// && (mTilesOverlay instanceof IOverlayMenuProvider)
|
||||
// && ((IOverlayMenuProvider) mTilesOverlay).isOptionsMenuEnabled()
|
||||
// && ((IOverlayMenuProvider) mTilesOverlay).onOptionsItemSelected(item,
|
||||
// menuIdOffset,
|
||||
// mapView)) {
|
||||
// return true;
|
||||
// }
|
||||
//
|
||||
// return false;
|
||||
// }
|
||||
}
|
||||
280
src/org/oscim/overlay/PathOverlay.java
Normal file
@ -0,0 +1,280 @@
|
||||
/*
|
||||
* Copyright 2012, osmdroid: Viesturs Zarins, Martin Pearman
|
||||
* Copyright 2012, Hannes Janetzek
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it under the
|
||||
* terms of the GNU Lesser 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 Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.oscim.overlay;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
import org.oscim.core.GeoPoint;
|
||||
import org.oscim.core.MapPosition;
|
||||
import org.oscim.core.MercatorProjection;
|
||||
import org.oscim.renderer.layer.Layer;
|
||||
import org.oscim.renderer.layer.LineLayer;
|
||||
import org.oscim.renderer.overlays.RenderOverlay;
|
||||
import org.oscim.theme.renderinstruction.Line;
|
||||
import org.oscim.view.MapView;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.Paint.Cap;
|
||||
|
||||
/** This class draws a path line in given color. */
|
||||
public class PathOverlay extends Overlay {
|
||||
|
||||
/** Stores points, converted to the map projection. */
|
||||
/* package */final ArrayList<GeoPoint> mPoints;
|
||||
/* package */boolean mUpdatePoints;
|
||||
|
||||
/** Paint settings. */
|
||||
protected Paint mPaint = new Paint();
|
||||
|
||||
class RenderPath extends RenderOverlay {
|
||||
|
||||
private static final byte MAX_ZOOM = 20;
|
||||
|
||||
// pre-projected points to zoomlovel 20
|
||||
private int[] mPreprojected;
|
||||
|
||||
// projected points
|
||||
private float[] mPPoints;
|
||||
private short[] mIndex;
|
||||
private int mSize;
|
||||
|
||||
private Line mLine;
|
||||
|
||||
// limit coords
|
||||
private final int max = 2048;
|
||||
|
||||
public RenderPath(MapView mapView) {
|
||||
super(mapView);
|
||||
mLine = new Line(Color.BLUE, 3.0f, Cap.BUTT);
|
||||
mIndex = new short[1];
|
||||
mPPoints = new float[1];
|
||||
}
|
||||
|
||||
// note: this is called from GL-Thread. so check your syncs!
|
||||
// TODO use an Overlay-Thread to build up layers (like for Labeling)
|
||||
@Override
|
||||
public synchronized void update(MapPosition curPos, boolean positionChanged,
|
||||
boolean tilesChanged) {
|
||||
|
||||
if (!tilesChanged && !mUpdatePoints)
|
||||
return;
|
||||
|
||||
float[] projected = mPPoints;
|
||||
|
||||
if (mUpdatePoints) {
|
||||
// pre-project point on zoomlelvel 20
|
||||
synchronized (mPoints) {
|
||||
mUpdatePoints = false;
|
||||
|
||||
ArrayList<GeoPoint> geopoints = mPoints;
|
||||
int size = geopoints.size();
|
||||
int[] points = mPreprojected;
|
||||
mSize = size * 2;
|
||||
|
||||
if (mSize > projected.length) {
|
||||
points = mPreprojected = new int[mSize];
|
||||
projected = mPPoints = new float[mSize];
|
||||
}
|
||||
|
||||
for (int i = 0, j = 0; i < size; i++, j += 2) {
|
||||
GeoPoint p = geopoints.get(i);
|
||||
points[j + 0] = (int) MercatorProjection.longitudeToPixelX(
|
||||
p.getLongitude(), MAX_ZOOM);
|
||||
points[j + 1] = (int) MercatorProjection.latitudeToPixelY(
|
||||
p.getLatitude(), MAX_ZOOM);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int size = mSize;
|
||||
|
||||
// keep position to render relative to current state
|
||||
updateMapPosition();
|
||||
|
||||
// items are placed relative to scale == 1
|
||||
mMapPosition.scale = 1;
|
||||
|
||||
// layers.clear();
|
||||
LineLayer ll = (LineLayer) layers.getLayer(1, Layer.LINE);
|
||||
// reset verticesCnt to reuse layer
|
||||
ll.verticesCnt = 0;
|
||||
ll.line = mLine;
|
||||
ll.width = 2.5f;
|
||||
|
||||
int x, y, px = 0, py = 0;
|
||||
int i = 0;
|
||||
|
||||
int diff = MAX_ZOOM - mMapPosition.zoomLevel;
|
||||
int mx = (int) mMapPosition.x;
|
||||
int my = (int) mMapPosition.y;
|
||||
|
||||
for (int j = 0; j < size; j += 2) {
|
||||
// TODO translate mapPosition and do this after clipping
|
||||
x = (mPreprojected[j + 0] >> diff) - mx;
|
||||
y = (mPreprojected[j + 1] >> diff) - my;
|
||||
|
||||
// TODO use line clipping, this doesnt work with 'GreatCircle'
|
||||
// TODO clip to view bounding box
|
||||
if (x > max || x < -max || y > max || y < -max) {
|
||||
if (i > 2) {
|
||||
mIndex[0] = (short) i;
|
||||
ll.addLine(projected, mIndex, false);
|
||||
}
|
||||
i = 0;
|
||||
continue;
|
||||
}
|
||||
|
||||
// skip too near points
|
||||
int dx = x - px;
|
||||
int dy = y - py;
|
||||
if ((i == 0) || dx > 2 || dx < -2 || dy > 2 || dy < -2) {
|
||||
projected[i + 0] = px = x;
|
||||
projected[i + 1] = py = y;
|
||||
i += 2;
|
||||
}
|
||||
}
|
||||
|
||||
mIndex[0] = (short) i;
|
||||
ll.addLine(projected, mIndex, false);
|
||||
|
||||
newData = true;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public PathOverlay(MapView mapView, final int color, final Context ctx) {
|
||||
super(ctx);
|
||||
this.mPaint.setColor(color);
|
||||
this.mPaint.setStrokeWidth(2.0f);
|
||||
this.mPaint.setStyle(Paint.Style.STROKE);
|
||||
|
||||
this.mPoints = new ArrayList<GeoPoint>();
|
||||
|
||||
mLayer = new RenderPath(mapView);
|
||||
}
|
||||
|
||||
public void setColor(final int color) {
|
||||
this.mPaint.setColor(color);
|
||||
}
|
||||
|
||||
public void setAlpha(final int a) {
|
||||
this.mPaint.setAlpha(a);
|
||||
}
|
||||
|
||||
public Paint getPaint() {
|
||||
return mPaint;
|
||||
}
|
||||
|
||||
public void setPaint(final Paint pPaint) {
|
||||
if (pPaint == null) {
|
||||
throw new IllegalArgumentException("pPaint argument cannot be null");
|
||||
}
|
||||
mPaint = pPaint;
|
||||
}
|
||||
|
||||
/** Draw a great circle. Calculate a point for every 100km along the path.
|
||||
* @param startPoint
|
||||
* start point of the great circle
|
||||
* @param endPoint
|
||||
* end point of the great circle */
|
||||
public void addGreatCircle(final GeoPoint startPoint, final GeoPoint endPoint) {
|
||||
synchronized (mPoints) {
|
||||
|
||||
// get the great circle path length in meters
|
||||
final int greatCircleLength = startPoint.distanceTo(endPoint);
|
||||
|
||||
// add one point for every 100kms of the great circle path
|
||||
final int numberOfPoints = greatCircleLength / 100000;
|
||||
|
||||
addGreatCircle(startPoint, endPoint, numberOfPoints);
|
||||
}
|
||||
}
|
||||
|
||||
/** Draw a great circle.
|
||||
* @param startPoint
|
||||
* start point of the great circle
|
||||
* @param endPoint
|
||||
* end point of the great circle
|
||||
* @param numberOfPoints
|
||||
* number of points to calculate along the path */
|
||||
public void addGreatCircle(final GeoPoint startPoint, final GeoPoint endPoint,
|
||||
final int numberOfPoints) {
|
||||
// adapted from page
|
||||
// http://compastic.blogspot.co.uk/2011/07/how-to-draw-great-circle-on-map-in.html
|
||||
// which was adapted from page http://maps.forum.nu/gm_flight_path.html
|
||||
|
||||
// convert to radians
|
||||
final double lat1 = startPoint.getLatitude() * Math.PI / 180;
|
||||
final double lon1 = startPoint.getLongitude() * Math.PI / 180;
|
||||
final double lat2 = endPoint.getLatitude() * Math.PI / 180;
|
||||
final double lon2 = endPoint.getLongitude() * Math.PI / 180;
|
||||
|
||||
final double d = 2 * Math.asin(Math.sqrt(Math.pow(Math.sin((lat1 - lat2) / 2), 2)
|
||||
+ Math.cos(lat1) * Math.cos(lat2)
|
||||
* Math.pow(Math.sin((lon1 - lon2) / 2), 2)));
|
||||
double bearing = Math.atan2(
|
||||
Math.sin(lon1 - lon2) * Math.cos(lat2),
|
||||
Math.cos(lat1) * Math.sin(lat2) - Math.sin(lat1) * Math.cos(lat2)
|
||||
* Math.cos(lon1 - lon2))
|
||||
/ -(Math.PI / 180);
|
||||
bearing = bearing < 0 ? 360 + bearing : bearing;
|
||||
|
||||
for (int i = 0, j = numberOfPoints + 1; i < j; i++) {
|
||||
final double f = 1.0 / numberOfPoints * i;
|
||||
final double A = Math.sin((1 - f) * d) / Math.sin(d);
|
||||
final double B = Math.sin(f * d) / Math.sin(d);
|
||||
final double x = A * Math.cos(lat1) * Math.cos(lon1) + B * Math.cos(lat2)
|
||||
* Math.cos(lon2);
|
||||
final double y = A * Math.cos(lat1) * Math.sin(lon1) + B * Math.cos(lat2)
|
||||
* Math.sin(lon2);
|
||||
final double z = A * Math.sin(lat1) + B * Math.sin(lat2);
|
||||
|
||||
final double latN = Math.atan2(z, Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2)));
|
||||
final double lonN = Math.atan2(y, x);
|
||||
addPoint((int) (latN / (Math.PI / 180) * 1E6), (int) (lonN / (Math.PI / 180) * 1E6));
|
||||
}
|
||||
}
|
||||
|
||||
public void clearPath() {
|
||||
synchronized (mPoints) {
|
||||
mPoints.clear();
|
||||
mUpdatePoints = true;
|
||||
}
|
||||
}
|
||||
|
||||
public void addPoint(final GeoPoint pt) {
|
||||
synchronized (mPoints) {
|
||||
this.mPoints.add(pt);
|
||||
mUpdatePoints = true;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public void addPoint(final int latitudeE6, final int longitudeE6) {
|
||||
synchronized (mPoints) {
|
||||
this.mPoints.add(new GeoPoint(latitudeE6, longitudeE6));
|
||||
mUpdatePoints = true;
|
||||
}
|
||||
}
|
||||
|
||||
public int getNumberOfPoints() {
|
||||
return this.mPoints.size();
|
||||
}
|
||||
}
|
||||
67
src/org/oscim/overlay/ResourceProxy.java
Normal file
@ -0,0 +1,67 @@
|
||||
package org.oscim.overlay;
|
||||
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.drawable.Drawable;
|
||||
|
||||
public interface ResourceProxy {
|
||||
|
||||
public static enum string {
|
||||
|
||||
// tile sources
|
||||
mapnik, cyclemap, public_transport, base, topo, hills, cloudmade_small, cloudmade_standard, mapquest_osm, mapquest_aerial, bing,
|
||||
|
||||
// overlays
|
||||
fiets_nl, base_nl, roads_nl,
|
||||
|
||||
// other stuff
|
||||
unknown, format_distance_meters, format_distance_kilometers, format_distance_miles, format_distance_nautical_miles, format_distance_feet, online_mode, offline_mode, my_location, compass, map_mode,
|
||||
|
||||
}
|
||||
|
||||
public static enum bitmap {
|
||||
|
||||
/**
|
||||
* For testing - the image doesn't exist.
|
||||
*/
|
||||
unknown,
|
||||
|
||||
center, direction_arrow, marker_default, marker_default_focused_base, navto_small, next, previous, person,
|
||||
|
||||
/**
|
||||
* Menu icons
|
||||
*/
|
||||
ic_menu_offline, ic_menu_mylocation, ic_menu_compass, ic_menu_mapmode
|
||||
}
|
||||
|
||||
String getString(string pResId);
|
||||
|
||||
/**
|
||||
* Use a string resource as a format definition, and format using the
|
||||
* supplied format arguments.
|
||||
*
|
||||
* @param pResId
|
||||
* ...
|
||||
* @param formatArgs
|
||||
* ...
|
||||
* @return ...
|
||||
*/
|
||||
String getString(string pResId, Object... formatArgs);
|
||||
|
||||
Bitmap getBitmap(bitmap pResId);
|
||||
|
||||
/**
|
||||
* Get a bitmap as a {@link Drawable}
|
||||
*
|
||||
* @param pResId
|
||||
* ...
|
||||
* @return ...
|
||||
*/
|
||||
Drawable getDrawable(bitmap pResId);
|
||||
|
||||
/**
|
||||
* Gets the density from the current screen's DisplayMetrics
|
||||
*
|
||||
* @return the screen's density
|
||||
*/
|
||||
float getDisplayMetricsDensity();
|
||||
}
|
||||
BIN
src/org/oscim/overlay/marker_default.png
Normal file
|
After Width: | Height: | Size: 1.9 KiB |
BIN
src/org/oscim/overlay/marker_default_focused_base.png
Normal file
|
After Width: | Height: | Size: 664 B |
@ -25,7 +25,7 @@ import static android.opengl.GLES20.GL_POLYGON_OFFSET_FILL;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
import java.nio.ShortBuffer;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
|
||||
import javax.microedition.khronos.egl.EGLConfig;
|
||||
@ -35,8 +35,7 @@ import org.oscim.core.MapPosition;
|
||||
import org.oscim.core.Tile;
|
||||
import org.oscim.renderer.layer.Layer;
|
||||
import org.oscim.renderer.layer.Layers;
|
||||
import org.oscim.renderer.overlays.Overlay;
|
||||
import org.oscim.renderer.overlays.OverlayText;
|
||||
import org.oscim.renderer.overlays.RenderOverlay;
|
||||
import org.oscim.theme.RenderTheme;
|
||||
import org.oscim.utils.GlUtils;
|
||||
import org.oscim.view.MapView;
|
||||
@ -61,7 +60,7 @@ public class GLRenderer implements GLSurfaceView.Renderer {
|
||||
|
||||
static int CACHE_TILES = CACHE_TILES_MAX;
|
||||
|
||||
private final MapView mMapView;
|
||||
private static MapView mMapView;
|
||||
static int mWidth, mHeight;
|
||||
|
||||
private static MapViewPosition mMapViewPosition;
|
||||
@ -177,10 +176,8 @@ public class GLRenderer implements GLSurfaceView.Renderer {
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @param mapView
|
||||
* the MapView
|
||||
*/
|
||||
/** @param mapView
|
||||
* the MapView */
|
||||
public GLRenderer(MapView mapView) {
|
||||
|
||||
mMapView = mapView;
|
||||
@ -213,15 +210,15 @@ public class GLRenderer implements GLSurfaceView.Renderer {
|
||||
shortBuffer[i].put(mFillCoords, 0, 8);
|
||||
}
|
||||
|
||||
mOverlays = new ArrayList<Overlay>();
|
||||
// overlays = new ArrayList<RenderOverlay>();
|
||||
|
||||
// mOverlays.add(new OverlayGrid(mapView));
|
||||
// mOverlays.add(new OverlayTest(mapView));
|
||||
mOverlays.add(new OverlayText(mapView));
|
||||
// overlays.add(new OverlayText(mapView));
|
||||
|
||||
}
|
||||
|
||||
private static ArrayList<Overlay> mOverlays;
|
||||
// private static ArrayList<RenderOverlay> overlays;
|
||||
|
||||
public static void setRenderTheme(RenderTheme t) {
|
||||
mClearColor = GlUtils.colorToFloat(t.getMapBackground());
|
||||
@ -234,7 +231,8 @@ public class GLRenderer implements GLSurfaceView.Renderer {
|
||||
|
||||
int newSize = layers.getSize();
|
||||
if (newSize == 0) {
|
||||
Log.d(TAG, "empty");
|
||||
// FIXME why are there so many tiles empty?
|
||||
// Log.d(TAG, "empty");
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -315,14 +313,14 @@ public class GLRenderer implements GLSurfaceView.Renderer {
|
||||
return tile.isReady;
|
||||
}
|
||||
|
||||
private static boolean uploadOverlayData(Overlay overlay) {
|
||||
private static boolean uploadOverlayData(RenderOverlay renderOverlay) {
|
||||
|
||||
if (uploadLayers(overlay.layers, overlay.vbo, true))
|
||||
overlay.isReady = true;
|
||||
if (uploadLayers(renderOverlay.layers, renderOverlay.vbo, true))
|
||||
renderOverlay.isReady = true;
|
||||
|
||||
overlay.newData = false;
|
||||
renderOverlay.newData = false;
|
||||
|
||||
return overlay.isReady;
|
||||
return renderOverlay.isReady;
|
||||
}
|
||||
|
||||
private static void checkBufferUsage() {
|
||||
@ -384,7 +382,7 @@ public class GLRenderer implements GLSurfaceView.Renderer {
|
||||
public void onDrawFrame(GL10 glUnused) {
|
||||
|
||||
// prevent main thread recreating all tiles (updateMap)
|
||||
// while rendering is going.
|
||||
// while rendering is going on.
|
||||
drawlock.lock();
|
||||
try {
|
||||
draw();
|
||||
@ -421,9 +419,11 @@ public class GLRenderer implements GLSurfaceView.Renderer {
|
||||
// get current tiles to draw
|
||||
mDrawTiles = TileManager.getActiveTiles(mDrawTiles);
|
||||
|
||||
// FIXME what if only drawing overlays?
|
||||
if (mDrawTiles == null || mDrawTiles.cnt == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
boolean tilesChanged = false;
|
||||
// check if the tiles have changed...
|
||||
if (serial != mDrawTiles.serial) {
|
||||
@ -450,14 +450,13 @@ public class GLRenderer implements GLSurfaceView.Renderer {
|
||||
float div = scaleDiv(tiles[0]);
|
||||
|
||||
// transform screen coordinates to tile coordinates
|
||||
float s = Tile.TILE_SIZE;
|
||||
float scale = mapPosition.scale / div;
|
||||
float px = (float) mapPosition.x * div;
|
||||
float py = (float) mapPosition.y * div;
|
||||
|
||||
for (int i = 0; i < 8; i += 2) {
|
||||
coords[i + 0] = (px + coords[i + 0] / scale) / s;
|
||||
coords[i + 1] = (py + coords[i + 1] / scale) / s;
|
||||
coords[i + 0] = (px + coords[i + 0] / scale) / Tile.TILE_SIZE;
|
||||
coords[i + 1] = (py + coords[i + 1] / scale) / Tile.TILE_SIZE;
|
||||
}
|
||||
|
||||
mHolderCount = 0;
|
||||
@ -514,11 +513,11 @@ public class GLRenderer implements GLSurfaceView.Renderer {
|
||||
|
||||
tilesChanged |= (uploadCnt > 0);
|
||||
|
||||
// if (changed || tilesChanged) {
|
||||
for (Overlay overlay : mOverlays) {
|
||||
overlay.update(changed, tilesChanged);
|
||||
}
|
||||
// }
|
||||
// update overlays
|
||||
List<RenderOverlay> overlays = mMapView.getOverlayManager().getRenderLayers();
|
||||
|
||||
for (int i = 0, n = overlays.size(); i < n; i++)
|
||||
overlays.get(i).update(mMapPosition, changed, tilesChanged);
|
||||
|
||||
GLES20.glEnable(GL_DEPTH_TEST);
|
||||
GLES20.glEnable(GL_POLYGON_OFFSET_FILL);
|
||||
@ -538,7 +537,6 @@ public class GLRenderer implements GLSurfaceView.Renderer {
|
||||
if (t.isVisible && !t.isReady && (t.holder == null))
|
||||
drawProxyTile(t);
|
||||
}
|
||||
// GlUtils.checkGlError("end draw");
|
||||
|
||||
GLES20.glDisable(GL_POLYGON_OFFSET_FILL);
|
||||
GLES20.glDisable(GL_DEPTH_TEST);
|
||||
@ -549,22 +547,25 @@ public class GLRenderer implements GLSurfaceView.Renderer {
|
||||
GLES20.glEnable(GL_BLEND);
|
||||
|
||||
// call overlay renderer
|
||||
for (Overlay overlay : mOverlays) {
|
||||
if (overlay.newData) {
|
||||
if (overlay.vbo == null)
|
||||
overlay.vbo = BufferObject.get();
|
||||
for (int i = 0, n = overlays.size(); i < n; i++) {
|
||||
RenderOverlay renderOverlay = overlays.get(i);
|
||||
|
||||
if (overlay.vbo == null)
|
||||
continue;
|
||||
if (renderOverlay.newData) {
|
||||
if (renderOverlay.vbo == null) {
|
||||
renderOverlay.vbo = BufferObject.get();
|
||||
|
||||
if (uploadOverlayData(overlay))
|
||||
overlay.isReady = true;
|
||||
if (renderOverlay.vbo == null)
|
||||
continue;
|
||||
}
|
||||
|
||||
if (uploadOverlayData(renderOverlay))
|
||||
renderOverlay.isReady = true;
|
||||
}
|
||||
if (!overlay.isReady)
|
||||
continue;
|
||||
|
||||
// setMatrix(mMVPMatrix, overlay);
|
||||
overlay.render(mMapPosition, mMVPMatrix, mProjMatrix);
|
||||
if (renderOverlay.isReady) {
|
||||
// setMatrix(mMVPMatrix, overlay);
|
||||
renderOverlay.render(mMapPosition, mMVPMatrix, mProjMatrix);
|
||||
}
|
||||
}
|
||||
|
||||
if (MapView.debugFrameTime) {
|
||||
@ -594,6 +595,9 @@ public class GLRenderer implements GLSurfaceView.Renderer {
|
||||
mapPosition.viewMatrix, 0);
|
||||
PolygonRenderer.debugDraw(mMVPMatrix, mDebugCoords, 1);
|
||||
}
|
||||
|
||||
// mMapView.getOverlayManager().onUpdate(mMapPosition);
|
||||
|
||||
}
|
||||
|
||||
// used to not draw a tile twice per frame.
|
||||
|
||||
@ -32,7 +32,7 @@ public class GLView extends GLSurfaceView {
|
||||
setEGLConfigChooser(new GlConfigChooser());
|
||||
setEGLContextClientVersion(2);
|
||||
|
||||
// setDebugFlags(DEBUG_CHECK_GL_ERROR | DEBUG_LOG_GL_CALLS);
|
||||
setDebugFlags(DEBUG_CHECK_GL_ERROR | DEBUG_LOG_GL_CALLS);
|
||||
mRenderer = new GLRenderer(mMapView);
|
||||
setRenderer(mRenderer);
|
||||
|
||||
|
||||
@ -44,6 +44,7 @@ class QuadTree {
|
||||
|
||||
static boolean remove(MapTile t) {
|
||||
if (t.rel == null) {
|
||||
// Bad Things(tm) happened
|
||||
Log.d(TAG, "already removed " + t);
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -13,12 +13,9 @@
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/* ported from Polymaps: Layer.js Copyright (c) 2010, SimpleGeo and Stamen Design */
|
||||
|
||||
package org.oscim.renderer;
|
||||
|
||||
import android.util.FloatMath;
|
||||
import android.util.Log;
|
||||
|
||||
public abstract class ScanBox {
|
||||
|
||||
@ -31,16 +28,14 @@ public abstract class ScanBox {
|
||||
this.y0 = y0;
|
||||
this.x1 = x1;
|
||||
this.y1 = y1;
|
||||
dx = x1 - x0;
|
||||
dy = y1 - y0;
|
||||
} else {
|
||||
this.x0 = x1;
|
||||
this.y0 = y1;
|
||||
this.x1 = x0;
|
||||
this.y1 = y0;
|
||||
dx = x0 - x1;
|
||||
dy = y0 - y1;
|
||||
}
|
||||
this.dx = this.x1 - this.x0;
|
||||
this.dy = this.y1 - this.y0;
|
||||
}
|
||||
}
|
||||
|
||||
@ -49,33 +44,32 @@ public abstract class ScanBox {
|
||||
private Edge ca = new Edge();
|
||||
protected byte mZoom;
|
||||
|
||||
void scan(float[] coords, byte zoom) {
|
||||
abstract void setVisible(int y, int x1, int x2);
|
||||
|
||||
public void scan(float[] coords, byte zoom) {
|
||||
mZoom = zoom;
|
||||
|
||||
// top-left -> top-right
|
||||
ab.set(coords[0], coords[1], coords[2], coords[3]);
|
||||
// top-right -> bottom-right
|
||||
bc.set(coords[2], coords[3], coords[4], coords[5]);
|
||||
// bottom-right -> bottom-left
|
||||
ca.set(coords[4], coords[5], coords[0], coords[1]);
|
||||
|
||||
scanTriangle();
|
||||
|
||||
ab.set(coords[4], coords[5], coords[6], coords[7]);
|
||||
bc.set(coords[6], coords[7], coords[0], coords[1]);
|
||||
ca.set(coords[0], coords[1], coords[4], coords[5]);
|
||||
scanTriangle();
|
||||
}
|
||||
// top-left -> bottom-right
|
||||
ab.set(coords[0], coords[1], coords[4], coords[5]);
|
||||
// bottom-right -> bottom-left
|
||||
bc.set(coords[4], coords[5], coords[6], coords[7]);
|
||||
// bottom-left -> top-left
|
||||
ca.set(coords[6], coords[7], coords[0], coords[1]);
|
||||
|
||||
/**
|
||||
* @param y
|
||||
* ...
|
||||
* @param x1
|
||||
* ...
|
||||
* @param x2
|
||||
* ...
|
||||
*/
|
||||
void setVisible(int y, int x1, int x2) {
|
||||
scanTriangle();
|
||||
}
|
||||
|
||||
private void scanTriangle() {
|
||||
|
||||
// sort so that ca.dy > bc.dy > ab.dy
|
||||
if (ab.dy > bc.dy) {
|
||||
Edge t = ab;
|
||||
ab = bc;
|
||||
@ -91,23 +85,21 @@ public abstract class ScanBox {
|
||||
bc = ca;
|
||||
ca = t;
|
||||
}
|
||||
// ca.dy > bc.dy > ab.dy
|
||||
|
||||
// shouldnt be possible, anyway
|
||||
if (ca.dy == 0)
|
||||
return;
|
||||
|
||||
if (ab.dy != 0)
|
||||
if (ab.dy > 0.1)
|
||||
scanSpans(ca, ab);
|
||||
|
||||
if (bc.dy != 0)
|
||||
if (bc.dy > 0.1)
|
||||
scanSpans(ca, bc);
|
||||
}
|
||||
|
||||
// FIXME
|
||||
private static final int MAX_SLOPE = 4;
|
||||
|
||||
private void scanSpans(Edge e0, Edge e1) {
|
||||
|
||||
// scan the y-range of the edge with less dy
|
||||
int y0 = (int) Math.max(0, FloatMath.floor(e1.y0));
|
||||
int y1 = (int) Math.min((1 << mZoom), FloatMath.ceil(e1.y1));
|
||||
|
||||
@ -131,41 +123,99 @@ public abstract class ScanBox {
|
||||
float m0 = e0.dx / e0.dy;
|
||||
float m1 = e1.dx / e1.dy;
|
||||
|
||||
// still needed?
|
||||
if (m0 > MAX_SLOPE)
|
||||
m0 = MAX_SLOPE;
|
||||
else if (m0 < -MAX_SLOPE)
|
||||
m0 = -MAX_SLOPE;
|
||||
// FIXME, something is wrong here, with a steep angle
|
||||
// 'fill' can shoot far over the area in one direction
|
||||
int maxSlope = 8;
|
||||
if (m0 > maxSlope)
|
||||
m0 = maxSlope;
|
||||
else if (m0 < -maxSlope)
|
||||
m0 = -maxSlope;
|
||||
|
||||
if (m1 > MAX_SLOPE)
|
||||
m1 = MAX_SLOPE;
|
||||
else if (m1 < -MAX_SLOPE)
|
||||
m1 = -MAX_SLOPE;
|
||||
if (m1 > maxSlope)
|
||||
m1 = maxSlope;
|
||||
else if (m1 < -maxSlope)
|
||||
m1 = -maxSlope;
|
||||
|
||||
// e0 goes to the right, e1 to the left
|
||||
int d0 = e0.dx > 0 ? 1 : 0; // use y + 1 to compute x0
|
||||
int d1 = e1.dx < 0 ? 1 : 0; // use y + 1 to compute x1
|
||||
|
||||
float x0, x1, dy;
|
||||
float dy;
|
||||
|
||||
for (int y = y0; y < y1; y++) {
|
||||
dy = y + d0 - e0.y0;
|
||||
if (e0.dy < dy)
|
||||
|
||||
dy = d0 + y - e0.y0;
|
||||
if (dy > e0.dy)
|
||||
dy = e0.dy;
|
||||
|
||||
x0 = e0.x0 + m0 * dy;
|
||||
x0 = FloatMath.ceil(x0);
|
||||
float x0 = FloatMath.ceil(e0.x0 + m0 * dy);
|
||||
|
||||
dy = y + d1 - e1.y0;
|
||||
if (e1.dy < dy)
|
||||
dy = d1 + y - e1.y0;
|
||||
if (dy > e1.dy)
|
||||
dy = e1.dy;
|
||||
|
||||
x1 = e1.x0 + m1 * dy;
|
||||
x1 = FloatMath.floor(x1);
|
||||
float x1 = FloatMath.floor(e1.x0 + m1 * dy);
|
||||
|
||||
if (x1 > x0)
|
||||
Log.d("...", "X set visible" + y + " " + x1 + "/" + x0);
|
||||
|
||||
setVisible(y, (int) x1, (int) x0);
|
||||
if (x1 < x0)
|
||||
setVisible(y, (int) x1, (int) x0);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* ported from Polymaps: Layer.js Copyright (c) 2010, SimpleGeo and Stamen
|
||||
* Design
|
||||
*/
|
||||
|
||||
// // scan-line conversion
|
||||
// function edge(a, b) {
|
||||
// if (a.row > b.row) { var t = a; a = b; b = t; }
|
||||
// return {
|
||||
// x0: a.column,
|
||||
// y0: a.row,
|
||||
// x1: b.column,
|
||||
// y1: b.row,
|
||||
// dx: b.column - a.column,
|
||||
// dy: b.row - a.row
|
||||
// };
|
||||
// }
|
||||
//
|
||||
// // scan-line conversion
|
||||
// function scanSpans(e0, e1, ymin, ymax, scanLine) {
|
||||
// var y0 = Math.max(ymin, Math.floor(e1.y0)),
|
||||
// y1 = Math.min(ymax, Math.ceil(e1.y1));
|
||||
//
|
||||
// // sort edges by x-coordinate
|
||||
// if ((e0.x0 == e1.x0 && e0.y0 == e1.y0)
|
||||
// ? (e0.x0 + e1.dy / e0.dy * e0.dx < e1.x1)
|
||||
// : (e0.x1 - e1.dy / e0.dy * e0.dx < e1.x0)) {
|
||||
// var t = e0; e0 = e1; e1 = t;
|
||||
// }
|
||||
//
|
||||
// // scan lines!
|
||||
// var m0 = e0.dx / e0.dy,
|
||||
// m1 = e1.dx / e1.dy,
|
||||
// d0 = e0.dx > 0, // use y + 1 to compute x0
|
||||
// d1 = e1.dx < 0; // use y + 1 to compute x1
|
||||
// for (var y = y0; y < y1; y++) {
|
||||
// var x0 = m0 * Math.max(0, Math.min(e0.dy, y + d0 - e0.y0)) + e0.x0,
|
||||
// x1 = m1 * Math.max(0, Math.min(e1.dy, y + d1 - e1.y0)) + e1.x0;
|
||||
// scanLine(Math.floor(x1), Math.ceil(x0), y);
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// // scan-line conversion
|
||||
// function scanTriangle(a, b, c, ymin, ymax, scanLine) {
|
||||
// var ab = edge(a, b),
|
||||
// bc = edge(b, c),
|
||||
// ca = edge(c, a);
|
||||
//
|
||||
// // sort edges by y-length
|
||||
// if (ab.dy > bc.dy) { var t = ab; ab = bc; bc = t; }
|
||||
// if (ab.dy > ca.dy) { var t = ab; ab = ca; ca = t; }
|
||||
// if (bc.dy > ca.dy) { var t = bc; bc = ca; ca = t; }
|
||||
//
|
||||
// // scan span! scan span!
|
||||
// if (ab.dy) scanSpans(ca, ab, ymin, ymax, scanLine);
|
||||
// if (bc.dy) scanSpans(ca, bc, ymin, ymax, scanLine);
|
||||
// }
|
||||
|
||||
}
|
||||
|
||||
@ -14,8 +14,9 @@
|
||||
*/
|
||||
package org.oscim.renderer;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Color;
|
||||
import android.opengl.GLES20;
|
||||
import android.opengl.GLUtils;
|
||||
@ -24,11 +25,12 @@ import android.util.Log;
|
||||
public class TextureObject {
|
||||
private static TextureObject pool;
|
||||
|
||||
private static ArrayList<Bitmap> mBitmaps;
|
||||
|
||||
// shared bitmap and canvas for default texture size
|
||||
public final static int TEXTURE_WIDTH = 256;
|
||||
public final static int TEXTURE_HEIGHT = 256;
|
||||
private static Bitmap[] mBitmap;
|
||||
private static Canvas[] mCanvas;
|
||||
|
||||
private static int mBitmapFormat;
|
||||
private static int mBitmapType;
|
||||
private static int objectCount = 10;
|
||||
@ -37,22 +39,38 @@ public class TextureObject {
|
||||
TextureObject to;
|
||||
|
||||
if (pool == null) {
|
||||
init(10);
|
||||
objectCount += 10;
|
||||
Log.d("...", "textures: " + objectCount);
|
||||
objectCount += 1;
|
||||
if (TextureRenderer.debug)
|
||||
Log.d("...", "textures: " + objectCount);
|
||||
pool = new TextureObject(-1);
|
||||
}
|
||||
|
||||
to = pool;
|
||||
pool = pool.next;
|
||||
|
||||
to.next = null;
|
||||
|
||||
to.bitmap = getBitmap();
|
||||
to.bitmap.eraseColor(Color.TRANSPARENT);
|
||||
|
||||
if (TextureRenderer.debug)
|
||||
Log.d("...", "get texture " + to.id + " " + to.bitmap);
|
||||
|
||||
return to;
|
||||
}
|
||||
|
||||
public static synchronized void release(TextureObject to) {
|
||||
|
||||
while (to != null) {
|
||||
if (TextureRenderer.debug)
|
||||
Log.d("...", "release texture " + to.id);
|
||||
|
||||
TextureObject next = to.next;
|
||||
|
||||
if (to.bitmap != null) {
|
||||
mBitmaps.add(to.bitmap);
|
||||
to.bitmap = null;
|
||||
}
|
||||
|
||||
to.next = pool;
|
||||
pool = to;
|
||||
|
||||
@ -60,6 +78,26 @@ public class TextureObject {
|
||||
}
|
||||
}
|
||||
|
||||
public static synchronized void uploadTexture(TextureObject to) {
|
||||
if (TextureRenderer.debug)
|
||||
Log.d("...", "upload texture " + to.id);
|
||||
|
||||
if (to.id < 0) {
|
||||
int[] textureIds = new int[1];
|
||||
GLES20.glGenTextures(1, textureIds, 0);
|
||||
to.id = textureIds[0];
|
||||
initTexture(to.id);
|
||||
if (TextureRenderer.debug)
|
||||
Log.d("...", "new texture " + to.id);
|
||||
}
|
||||
|
||||
uploadTexture(to, to.bitmap, mBitmapFormat, mBitmapType,
|
||||
TEXTURE_WIDTH, TEXTURE_HEIGHT);
|
||||
|
||||
mBitmaps.add(to.bitmap);
|
||||
to.bitmap = null;
|
||||
}
|
||||
|
||||
public static void uploadTexture(TextureObject to, Bitmap bitmap,
|
||||
int format, int type, int w, int h) {
|
||||
|
||||
@ -77,73 +115,74 @@ public class TextureObject {
|
||||
}
|
||||
}
|
||||
|
||||
static void initTexture(int id) {
|
||||
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, id);
|
||||
|
||||
GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER,
|
||||
GLES20.GL_LINEAR);
|
||||
GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER,
|
||||
GLES20.GL_LINEAR);
|
||||
GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S,
|
||||
GLES20.GL_CLAMP_TO_EDGE); // Set U Wrapping
|
||||
GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T,
|
||||
GLES20.GL_CLAMP_TO_EDGE); // Set V Wrapping
|
||||
}
|
||||
|
||||
static void init(int num) {
|
||||
pool = null;
|
||||
|
||||
TextureObject to;
|
||||
|
||||
int[] textureIds = new int[num];
|
||||
GLES20.glGenTextures(num, textureIds, 0);
|
||||
|
||||
for (int i = 1; i < num; i++) {
|
||||
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureIds[i]);
|
||||
|
||||
GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER,
|
||||
GLES20.GL_LINEAR);
|
||||
GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER,
|
||||
GLES20.GL_LINEAR);
|
||||
GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S,
|
||||
GLES20.GL_CLAMP_TO_EDGE); // Set U Wrapping
|
||||
GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T,
|
||||
GLES20.GL_CLAMP_TO_EDGE); // Set V Wrapping
|
||||
|
||||
initTexture(textureIds[i]);
|
||||
to = new TextureObject(textureIds[i]);
|
||||
|
||||
to.next = pool;
|
||||
pool = to;
|
||||
}
|
||||
|
||||
mBitmap = new Bitmap[4];
|
||||
mCanvas = new Canvas[4];
|
||||
mBitmaps = new ArrayList<Bitmap>(10);
|
||||
|
||||
for (int i = 0; i < 4; i++) {
|
||||
mBitmap[i] = Bitmap.createBitmap(TEXTURE_WIDTH, TEXTURE_HEIGHT,
|
||||
Bitmap bitmap = Bitmap.createBitmap(TEXTURE_WIDTH, TEXTURE_HEIGHT,
|
||||
Bitmap.Config.ARGB_8888);
|
||||
mCanvas[i] = new Canvas(mBitmap[i]);
|
||||
|
||||
mBitmaps.add(bitmap);
|
||||
}
|
||||
mBitmapFormat = GLUtils.getInternalFormat(mBitmap[0]);
|
||||
mBitmapType = GLUtils.getType(mBitmap[0]);
|
||||
|
||||
mBitmapFormat = GLUtils.getInternalFormat(mBitmaps.get(0));
|
||||
mBitmapType = GLUtils.getType(mBitmaps.get(0));
|
||||
}
|
||||
|
||||
private static int curCanvas = 0;
|
||||
private static Bitmap getBitmap() {
|
||||
int size = mBitmaps.size();
|
||||
if (size == 0) {
|
||||
for (int i = 0; i < 4; i++) {
|
||||
Bitmap bitmap = Bitmap.createBitmap(TEXTURE_WIDTH, TEXTURE_HEIGHT,
|
||||
Bitmap.Config.ARGB_8888);
|
||||
|
||||
public static Canvas getCanvas() {
|
||||
curCanvas = ++curCanvas % 4;
|
||||
|
||||
mBitmap[curCanvas].eraseColor(Color.TRANSPARENT);
|
||||
|
||||
return mCanvas[curCanvas];
|
||||
}
|
||||
|
||||
public static TextureObject uploadCanvas(short offset, short indices) {
|
||||
TextureObject to = get();
|
||||
uploadTexture(to, mBitmap[curCanvas],
|
||||
mBitmapFormat, mBitmapType,
|
||||
TEXTURE_WIDTH, TEXTURE_HEIGHT);
|
||||
|
||||
to.offset = offset;
|
||||
to.vertices = (short) (indices - offset);
|
||||
|
||||
return to;
|
||||
mBitmaps.add(bitmap);
|
||||
}
|
||||
size = 4;
|
||||
}
|
||||
return mBitmaps.remove(size - 1);
|
||||
}
|
||||
|
||||
public TextureObject next;
|
||||
|
||||
public Bitmap bitmap;
|
||||
|
||||
int id;
|
||||
int width;
|
||||
int height;
|
||||
|
||||
// vertex offset from which this texture is referenced
|
||||
// or store texture id with vertex?
|
||||
short offset;
|
||||
short vertices;
|
||||
public short offset;
|
||||
public short vertices;
|
||||
|
||||
TextureObject(int id) {
|
||||
this.id = id;
|
||||
|
||||
@ -24,8 +24,11 @@ import org.oscim.renderer.layer.TextureLayer;
|
||||
import org.oscim.utils.GlUtils;
|
||||
|
||||
import android.opengl.GLES20;
|
||||
import android.util.Log;
|
||||
|
||||
public final class TextureRenderer {
|
||||
public final static boolean debug = false;
|
||||
|
||||
private static int mTextureProgram;
|
||||
private static int hTextureMVMatrix;
|
||||
private static int hTextureProjMatrix;
|
||||
@ -35,7 +38,7 @@ public final class TextureRenderer {
|
||||
private static int hTextureTexCoord;
|
||||
private static int mIndicesVBO;
|
||||
|
||||
final static int INDICES_PER_SPRITE = 6;
|
||||
public final static int INDICES_PER_SPRITE = 6;
|
||||
final static int VERTICES_PER_SPRITE = 4;
|
||||
final static int SHORTS_PER_VERTICE = 6;
|
||||
// per texture
|
||||
@ -89,9 +92,9 @@ public final class TextureRenderer {
|
||||
|
||||
public static Layer draw(Layer layer, float scale, float[] projection,
|
||||
float matrix[], int offset) {
|
||||
GlUtils.checkGlError("draw texture0");
|
||||
|
||||
// GlUtils.checkGlError("draw texture >");
|
||||
GLES20.glUseProgram(mTextureProgram);
|
||||
GlUtils.checkGlError("draw texture1");
|
||||
|
||||
int va = hTextureTexCoord;
|
||||
if (!GLRenderer.vertexArray[va]) {
|
||||
@ -106,39 +109,48 @@ public final class TextureRenderer {
|
||||
}
|
||||
|
||||
TextureLayer tl = (TextureLayer) layer;
|
||||
GlUtils.checkGlError("draw texture2.");
|
||||
GLES20.glUniform1f(hTextureScale, scale);
|
||||
|
||||
if (tl.fixed)
|
||||
GLES20.glUniform1f(hTextureScale, scale);
|
||||
else
|
||||
GLES20.glUniform1f(hTextureScale, 1);
|
||||
|
||||
GLES20.glUniform1f(hTextureScreenScale, 1f / GLRenderer.mWidth);
|
||||
|
||||
GLES20.glUniformMatrix4fv(hTextureProjMatrix, 1, false, projection, 0);
|
||||
GLES20.glUniformMatrix4fv(hTextureMVMatrix, 1, false, matrix, 0);
|
||||
GlUtils.checkGlError("draw texture2");
|
||||
|
||||
GLES20.glBindBuffer(GLES20.GL_ELEMENT_ARRAY_BUFFER, mIndicesVBO);
|
||||
GlUtils.checkGlError("draw texture3");
|
||||
|
||||
for (TextureObject to = tl.textures; to != null; to = to.next) {
|
||||
if (TextureRenderer.debug)
|
||||
Log.d("...", "draw texture: " + to.id + " " + to.offset + " " + to.vertices);
|
||||
|
||||
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, to.id);
|
||||
GlUtils.checkGlError("draw texture4");
|
||||
int maxVertices = MAX_ITEMS * INDICES_PER_SPRITE;
|
||||
|
||||
GlUtils.checkGlError("draw texture5");
|
||||
for (int i = 0; i < to.vertices; i += maxVertices) {
|
||||
// to.offset * (24(shorts) * 2(short-bytes) / 6(indices) == 8)
|
||||
int off = (to.offset + i) * 8 + offset;
|
||||
|
||||
// to.offset * 24(shorts) * 2(short-bytes) / 6(indices)
|
||||
GLES20.glVertexAttribPointer(hTextureVertex, 4,
|
||||
GLES20.GL_SHORT, false, 12, to.offset * 8 + offset);
|
||||
GlUtils.checkGlError("draw texture..");
|
||||
GLES20.glVertexAttribPointer(hTextureVertex, 4,
|
||||
GLES20.GL_SHORT, false, 12, off);
|
||||
|
||||
GLES20.glVertexAttribPointer(hTextureTexCoord, 2,
|
||||
GLES20.GL_SHORT, false, 12, to.offset * 8 + offset + 8);
|
||||
GlUtils.checkGlError("draw texture...");
|
||||
GLES20.glVertexAttribPointer(hTextureTexCoord, 2,
|
||||
GLES20.GL_SHORT, false, 12, off + 8);
|
||||
|
||||
GLES20.glDrawElements(GLES20.GL_TRIANGLES, to.vertices,
|
||||
GLES20.GL_UNSIGNED_SHORT, 0);
|
||||
int numVertices = to.vertices - i;
|
||||
if (numVertices > maxVertices)
|
||||
numVertices = maxVertices;
|
||||
|
||||
GLES20.glDrawElements(GLES20.GL_TRIANGLES, numVertices,
|
||||
GLES20.GL_UNSIGNED_SHORT, 0);
|
||||
}
|
||||
}
|
||||
|
||||
GLES20.glBindBuffer(GLES20.GL_ELEMENT_ARRAY_BUFFER, 0);
|
||||
GlUtils.checkGlError("draw texture");
|
||||
|
||||
// GlUtils.checkGlError("< draw texture");
|
||||
|
||||
return layer.next;
|
||||
}
|
||||
|
||||
@ -308,7 +308,7 @@ public class TileGenerator implements IRenderCallback, IMapDatabaseCallback {
|
||||
|
||||
SymbolLayer sl = (SymbolLayer) mLayers.textureLayers;
|
||||
|
||||
SymbolItem it = new SymbolItem();
|
||||
SymbolItem it = SymbolItem.get();
|
||||
it.x = mPoiX;
|
||||
it.y = mPoiY;
|
||||
it.bitmap = bitmap;
|
||||
|
||||
@ -23,16 +23,13 @@ import org.oscim.core.Tile;
|
||||
import org.oscim.generator.JobTile;
|
||||
import org.oscim.renderer.layer.TextItem;
|
||||
import org.oscim.renderer.layer.VertexPool;
|
||||
import org.oscim.renderer.overlays.Overlay;
|
||||
import org.oscim.view.MapView;
|
||||
import org.oscim.view.MapViewPosition;
|
||||
|
||||
import android.util.FloatMath;
|
||||
import android.util.Log;
|
||||
|
||||
// FIXME move GLSurfaceView in separate class
|
||||
public class TileManager { // extends GLSurfaceView {
|
||||
private final static String TAG = "TileManager";
|
||||
public class TileManager {
|
||||
static final String TAG = TileManager.class.getSimpleName();
|
||||
|
||||
private static final int MAX_TILES_IN_QUEUE = 40;
|
||||
private static final int CACHE_THRESHOLD = 10;
|
||||
@ -45,17 +42,12 @@ public class TileManager { // extends GLSurfaceView {
|
||||
// new jobs for the MapWorkers
|
||||
private static ArrayList<JobTile> mJobList;
|
||||
|
||||
// all tiles currently referenced
|
||||
// all tiles
|
||||
private static ArrayList<MapTile> mTiles;
|
||||
|
||||
// tiles that have new data to upload, see passTile()
|
||||
private static ArrayList<MapTile> mTilesLoaded;
|
||||
|
||||
private static ArrayList<Overlay> mOverlays;
|
||||
|
||||
// TODO current boundary tiles, values used to check if position has
|
||||
// changed for updating current tile list
|
||||
|
||||
private static boolean mInitial;
|
||||
|
||||
// private static MapPosition mCurPosition, mDrawPosition;
|
||||
@ -67,8 +59,6 @@ public class TileManager { // extends GLSurfaceView {
|
||||
|
||||
private static float[] mTileCoords = new float[8];
|
||||
|
||||
// private static int[] mBoundaryTiles = new int[8];
|
||||
|
||||
static int mUpdateCnt;
|
||||
static ReentrantLock tilelock = new ReentrantLock();
|
||||
static Tiles mCurrentTiles;
|
||||
@ -123,10 +113,6 @@ public class TileManager { // extends GLSurfaceView {
|
||||
}
|
||||
};
|
||||
|
||||
// why not try a pattern every now and then?
|
||||
// but should do the same for GLRenderer.
|
||||
// findbugs found that volatile thingy, though this class
|
||||
// is created before any other thread starts
|
||||
private static volatile TileManager SINGLETON;
|
||||
|
||||
public static TileManager create(MapView mapView) {
|
||||
@ -147,27 +133,15 @@ public class TileManager { // extends GLSurfaceView {
|
||||
}
|
||||
|
||||
private TileManager(MapView mapView) {
|
||||
// super(context);
|
||||
|
||||
mMapView = mapView;
|
||||
mMapViewPosition = mapView.getMapViewPosition();
|
||||
|
||||
// Log.d(TAG, "init GLSurfaceLayer");
|
||||
// setEGLConfigChooser(new GlConfigChooser());
|
||||
// setEGLContextClientVersion(2);
|
||||
//
|
||||
// // setDebugFlags(DEBUG_CHECK_GL_ERROR | DEBUG_LOG_GL_CALLS);
|
||||
// mRenderer = new GLRenderer(mMapView);
|
||||
// setRenderer(mRenderer);
|
||||
//
|
||||
// // if (!debugFrameTime)
|
||||
// setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY);
|
||||
|
||||
mJobList = new ArrayList<JobTile>();
|
||||
mTiles = new ArrayList<MapTile>();
|
||||
mTilesLoaded = new ArrayList<MapTile>(30);
|
||||
mOverlays = new ArrayList<Overlay>(5);
|
||||
|
||||
// this is probably a good place to init these
|
||||
VertexPool.init();
|
||||
QuadTree.init();
|
||||
|
||||
@ -191,6 +165,7 @@ public class TileManager { // extends GLSurfaceView {
|
||||
if (mMapView == null)
|
||||
return;
|
||||
|
||||
// FIXME too messy!
|
||||
if (clear || mInitial) {
|
||||
// make sure onDrawFrame is not running
|
||||
GLRenderer.drawlock.lock();
|
||||
@ -209,10 +184,6 @@ public class TileManager { // extends GLSurfaceView {
|
||||
|
||||
QuadTree.init();
|
||||
|
||||
// TODO clear overlay items data
|
||||
mOverlays.clear();
|
||||
// mOverlays.add(new Overlay());
|
||||
|
||||
// set up TileData arrays that are passed to gl-thread
|
||||
int num = mWidth;
|
||||
if (mWidth < mHeight)
|
||||
@ -246,11 +217,13 @@ public class TileManager { // extends GLSurfaceView {
|
||||
}
|
||||
|
||||
float s = Tile.TILE_SIZE;
|
||||
// load some additional tiles more than currently visible
|
||||
// load some tiles more than currently visible
|
||||
// TODO limit how many more...
|
||||
float scale = mapPosition.scale * 0.75f;
|
||||
float px = (float) mapPosition.x;
|
||||
float py = (float) mapPosition.y;
|
||||
|
||||
// TODO hint whether to prefetch parent / children
|
||||
int zdir = 0;
|
||||
|
||||
for (int i = 0; i < 8; i += 2) {
|
||||
@ -258,9 +231,6 @@ public class TileManager { // extends GLSurfaceView {
|
||||
coords[i + 1] = (py + coords[i + 1] / scale) / s;
|
||||
}
|
||||
|
||||
// TODO all following should probably be done in an idler instead
|
||||
// to drain queued events. need to check how android handles things.
|
||||
|
||||
boolean changed = updateVisibleList(mapPosition, zdir);
|
||||
|
||||
mMapView.render();
|
||||
@ -276,6 +246,8 @@ public class TileManager { // extends GLSurfaceView {
|
||||
}
|
||||
|
||||
public static Tiles getActiveTiles(Tiles td) {
|
||||
if (mCurrentTiles == null)
|
||||
return td;
|
||||
|
||||
if (td != null && td.serial == mUpdateCnt)
|
||||
return td;
|
||||
@ -409,58 +381,65 @@ public class TileManager { // extends GLSurfaceView {
|
||||
|
||||
tile = QuadTree.getTile(x, y, zoomLevel);
|
||||
|
||||
if (tile == null) {
|
||||
tile = new MapTile(x, y, zoomLevel);
|
||||
QuadTree.add(tile);
|
||||
if (tile != null) {
|
||||
if (!tile.isActive())
|
||||
mJobList.add(tile);
|
||||
|
||||
mTiles.add(tile);
|
||||
mJobList.add(tile);
|
||||
tileCounter++;
|
||||
} else if (!tile.isActive()) {
|
||||
mJobList.add(tile);
|
||||
return tile;
|
||||
}
|
||||
// mNewTiles.tiles[tiles++] = tile;
|
||||
|
||||
// if (fetchChildren) {
|
||||
// byte z = (byte) (zoomLevel + 1);
|
||||
// for (int i = 0; i < 4; i++) {
|
||||
// int cx = (xx << 1) + (i % 2);
|
||||
// int cy = (yy << 1) + (i >> 1);
|
||||
//
|
||||
// MapTile c = QuadTree.getTile(cx, cy, z);
|
||||
//
|
||||
// if (c == null) {
|
||||
// c = new MapTile(cx, cy, z);
|
||||
//
|
||||
// QuadTree.add(c);
|
||||
// mTiles.add(c);
|
||||
// }
|
||||
//
|
||||
// if (!c.isActive()) {
|
||||
// mJobList.add(c);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
tile = new MapTile(x, y, zoomLevel);
|
||||
QuadTree.add(tile);
|
||||
|
||||
// if (fetchParent || (!fetchProxy && zdir > 0 && zoomLevel > 0)) {
|
||||
mTiles.add(tile);
|
||||
mJobList.add(tile);
|
||||
tileCounter++;
|
||||
|
||||
// if (zdir > 0 && zoomLevel > 0) {
|
||||
// // prefetch parent
|
||||
// MapTile p = tile.rel.parent.tile;
|
||||
//
|
||||
// if (p == null) {
|
||||
// p = new MapTile(x >> 1, y >> 1, (byte) (zoomLevel - 1));
|
||||
//
|
||||
// QuadTree.add(p);
|
||||
// mTiles.add(p);
|
||||
// mJobList.add(p);
|
||||
//
|
||||
// } else if (!p.isActive()) {
|
||||
// if (!mJobList.contains(p))
|
||||
// mJobList.add(p);
|
||||
// }
|
||||
// }
|
||||
return tile;
|
||||
|
||||
// mNewTiles.tiles[tiles++] = tile;
|
||||
// boolean fetchParent = false;
|
||||
// boolean fetchProxy = false;
|
||||
// boolean fetchChildren = false;
|
||||
// if (fetchChildren) {
|
||||
// byte z = (byte) (zoomLevel + 1);
|
||||
// for (int i = 0; i < 4; i++) {
|
||||
// int cx = (x << 1) + (i % 2);
|
||||
// int cy = (y << 1) + (i >> 1);
|
||||
//
|
||||
// MapTile c = QuadTree.getTile(cx, cy, z);
|
||||
//
|
||||
// if (c == null) {
|
||||
// c = new MapTile(cx, cy, z);
|
||||
//
|
||||
// QuadTree.add(c);
|
||||
// mTiles.add(c);
|
||||
// }
|
||||
//
|
||||
// if (!c.isActive()) {
|
||||
// mJobList.add(c);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// if (fetchParent || (!fetchProxy && zdir > 0 && zoomLevel > 0)) {
|
||||
// if (zdir > 0 && zoomLevel > 0) {
|
||||
// // prefetch parent
|
||||
// MapTile p = tile.rel.parent.tile;
|
||||
//
|
||||
// if (p == null) {
|
||||
// p = new MapTile(x >> 1, y >> 1, (byte) (zoomLevel - 1));
|
||||
//
|
||||
// QuadTree.add(p);
|
||||
// mTiles.add(p);
|
||||
// mJobList.add(p);
|
||||
//
|
||||
// } else if (!p.isActive()) {
|
||||
// if (!mJobList.contains(p))
|
||||
// mJobList.add(p);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
private static void clearTile(MapTile t) {
|
||||
@ -492,7 +471,7 @@ public class TileManager { // extends GLSurfaceView {
|
||||
byte zoom = mapPosition.zoomLevel;
|
||||
long x = (long) mapPosition.x;
|
||||
long y = (long) mapPosition.y;
|
||||
// long center = Tile.TILE_SIZE << (zoom - 1);
|
||||
long center = Tile.TILE_SIZE << (zoom - 1);
|
||||
int diff;
|
||||
long dx, dy;
|
||||
|
||||
@ -506,11 +485,10 @@ public class TileManager { // extends GLSurfaceView {
|
||||
if (diff == 0) {
|
||||
dx = (t.pixelX + h) - x;
|
||||
dy = (t.pixelY + h) - y;
|
||||
// t.distance = ((dx > 0 ? dx : -dx) + (dy > 0 ? dy : -dy)) *
|
||||
// 0.25f;
|
||||
// dx %= center;
|
||||
// dy %= center;
|
||||
t.distance = FloatMath.sqrt((dx * dx + dy * dy)) * 0.25f;
|
||||
dx %= center;
|
||||
dy %= center;
|
||||
t.distance = ((dx > 0 ? dx : -dx) + (dy > 0 ? dy : -dy)) * 0.25f;
|
||||
// t.distance = FloatMath.sqrt((dx * dx + dy * dy)) * 0.25f;
|
||||
} else if (diff > 0) {
|
||||
// tile zoom level is child of current
|
||||
|
||||
@ -522,20 +500,19 @@ public class TileManager { // extends GLSurfaceView {
|
||||
dx = ((t.pixelX + h) >> (diff >> 1)) - x;
|
||||
dy = ((t.pixelY + h) >> (diff >> 1)) - y;
|
||||
}
|
||||
// dx %= center;
|
||||
// dy %= center;
|
||||
// t.distance = ((dx > 0 ? dx : -dx) + (dy > 0 ? dy : -dy));
|
||||
t.distance = FloatMath.sqrt((dx * dx + dy * dy));
|
||||
dx %= center;
|
||||
dy %= center;
|
||||
t.distance = ((dx > 0 ? dx : -dx) + (dy > 0 ? dy : -dy));
|
||||
// t.distance = FloatMath.sqrt((dx * dx + dy * dy));
|
||||
|
||||
} else {
|
||||
// tile zoom level is parent of current
|
||||
dx = ((t.pixelX + h) << -diff) - x;
|
||||
dy = ((t.pixelY + h) << -diff) - y;
|
||||
// dx %= center;
|
||||
// dy %= center;
|
||||
// t.distance = ((dx > 0 ? dx : -dx) + (dy > 0 ? dy : -dy)) *
|
||||
// (-diff * 0.5f);
|
||||
t.distance = FloatMath.sqrt((dx * dx + dy * dy)) * (-diff * 0.5f);
|
||||
dx %= center;
|
||||
dy %= center;
|
||||
t.distance = ((dx > 0 ? dx : -dx) + (dy > 0 ? dy : -dy)) * (-diff * 0.5f);
|
||||
// t.distance = FloatMath.sqrt((dx * dx + dy * dy)) * (-diff * 0.5f);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -565,10 +542,9 @@ public class TileManager { // extends GLSurfaceView {
|
||||
|
||||
for (int i = 1; i < remove; i++) {
|
||||
MapTile t = mTiles.remove(size - i);
|
||||
// synchronized (t) {
|
||||
|
||||
if (t.isLocked()) {
|
||||
// dont remove tile used by GLRenderer
|
||||
// dont remove tile used by GLRenderer, or somewhere else
|
||||
Log.d(TAG, "X not removing " + t + " " + t.distance);
|
||||
mTiles.add(t);
|
||||
} else if (t.isLoading) {
|
||||
@ -585,7 +561,6 @@ public class TileManager { // extends GLSurfaceView {
|
||||
} else {
|
||||
clearTile(t);
|
||||
}
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
@ -616,7 +591,7 @@ public class TileManager { // extends GLSurfaceView {
|
||||
for (int i = 0, n = size - MAX_TILES_IN_QUEUE / 2; i < n; n--) {
|
||||
|
||||
MapTile t = mTilesLoaded.get(i);
|
||||
// synchronized (t) {
|
||||
|
||||
if (t.isLocked()) {
|
||||
// Log.d(TAG, "keep unused tile data: " + t + " " +
|
||||
// t.isActive);
|
||||
@ -629,7 +604,6 @@ public class TileManager { // extends GLSurfaceView {
|
||||
mTiles.remove(t);
|
||||
clearTile(t);
|
||||
}
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
@ -650,21 +624,22 @@ public class TileManager { // extends GLSurfaceView {
|
||||
// return true;
|
||||
// }
|
||||
|
||||
// synchronized (tile) {
|
||||
if (tile.vbo == null) {
|
||||
tile.vbo = BufferObject.get();
|
||||
|
||||
if (tile.vbo == null) {
|
||||
Log.d(TAG, "no VBOs left for " + tile);
|
||||
tile.isLoading = false;
|
||||
return false;
|
||||
}
|
||||
} else
|
||||
if (tile.vbo != null) {
|
||||
// BAD Things(tm) happend...
|
||||
Log.d(TAG, "tile loaded before " + tile);
|
||||
return true;
|
||||
}
|
||||
|
||||
tile.vbo = BufferObject.get();
|
||||
|
||||
if (tile.vbo == null) {
|
||||
Log.d(TAG, "no VBOs left for " + tile);
|
||||
tile.isLoading = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
tile.newData = true;
|
||||
tile.isLoading = false;
|
||||
// }
|
||||
|
||||
mMapView.render();
|
||||
|
||||
@ -685,23 +660,4 @@ public class TileManager { // extends GLSurfaceView {
|
||||
if (mWidth > 0 && mHeight > 0)
|
||||
mInitial = true;
|
||||
}
|
||||
|
||||
// public void setRenderTheme(RenderTheme t) {
|
||||
// if (mRenderer != null)
|
||||
// mRenderer.setRenderTheme(t);
|
||||
//
|
||||
// }
|
||||
|
||||
// @Override
|
||||
// protected void onSizeChanged(int w, int h, int oldw, int oldh) {
|
||||
//
|
||||
// Log.d(TAG, "onSizeChanged" + w + " " + h);
|
||||
// mWidth = w;
|
||||
// mHeight = h;
|
||||
//
|
||||
// if (mWidth > 0 && mHeight > 0)
|
||||
// mInitial = true;
|
||||
//
|
||||
// super.onSizeChanged(w, h, oldw, oldh);
|
||||
// }
|
||||
}
|
||||
|
||||
@ -35,6 +35,5 @@ public abstract class Layer {
|
||||
VertexPoolItem pool;
|
||||
protected VertexPoolItem curItem;
|
||||
|
||||
protected void clear() {
|
||||
}
|
||||
abstract protected void clear();
|
||||
}
|
||||
|
||||
@ -118,6 +118,10 @@ public class Layers {
|
||||
TextureLayer sl = (TextureLayer) l;
|
||||
sl.compile(sbuf);
|
||||
}
|
||||
|
||||
// FIXME
|
||||
addLayerItems(sbuf, textureLayers, Layer.SYMBOL, 0);
|
||||
|
||||
}
|
||||
|
||||
private static void addLayerItems(ShortBuffer sbuf, Layer l, byte type, int pos) {
|
||||
@ -162,14 +166,22 @@ public class Layers {
|
||||
layers = layers.next;
|
||||
}
|
||||
|
||||
while (textureLayers != null) {
|
||||
textureLayers.clear();
|
||||
Layer l = textureLayers;
|
||||
while (l != null) {
|
||||
|
||||
// TextureLayer sl = (TextureLayer) textureLayers;
|
||||
// if (sl.textures != null)
|
||||
// TextureObject.release(sl.textures);
|
||||
l.clear();
|
||||
|
||||
textureLayers = textureLayers.next;
|
||||
if (l.pool != null) {
|
||||
VertexPool.release(l.pool);
|
||||
l.pool = null;
|
||||
l.curItem = null;
|
||||
}
|
||||
|
||||
// if (l instanceof TextLayer)
|
||||
// ((TextLayer) l).clear();
|
||||
|
||||
l = l.next;
|
||||
}
|
||||
textureLayers = null;
|
||||
}
|
||||
}
|
||||
|
||||
@ -49,11 +49,18 @@ public final class LineLayer extends Layer {
|
||||
outlines = link;
|
||||
}
|
||||
|
||||
/*
|
||||
/**
|
||||
* line extrusion is based on code from GLMap
|
||||
* (https://github.com/olofsj/GLMap/) by olofsj
|
||||
*
|
||||
* @param points
|
||||
* array of points as float x_n = i, y_n = i+1
|
||||
* @param index
|
||||
* array of line indices holding the length of the individual
|
||||
* lines
|
||||
* @param closed
|
||||
* whether to connect start- and end-point
|
||||
*/
|
||||
|
||||
public void addLine(float[] points, short[] index, boolean closed) {
|
||||
float x, y, nextX, nextY, prevX, prevY;
|
||||
float a, ux, uy, vx, vy, wx, wy;
|
||||
@ -83,7 +90,7 @@ public final class LineLayer extends Layer {
|
||||
if (length < 0)
|
||||
break;
|
||||
|
||||
// save some vertices
|
||||
// Note: just a hack to save some vertices
|
||||
if (rounded && i > 200)
|
||||
rounded = false;
|
||||
|
||||
@ -520,4 +527,8 @@ public final class LineLayer extends Layer {
|
||||
si.used = opos;
|
||||
curItem = si;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void clear() {
|
||||
}
|
||||
}
|
||||
|
||||
@ -87,4 +87,7 @@ public final class PolygonLayer extends Layer {
|
||||
curItem = si;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void clear() {
|
||||
}
|
||||
}
|
||||
|
||||
@ -15,15 +15,55 @@
|
||||
package org.oscim.renderer.layer;
|
||||
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.drawable.Drawable;
|
||||
|
||||
public class SymbolItem {
|
||||
private static Object lock = new Object();
|
||||
private static SymbolItem pool;
|
||||
|
||||
public static SymbolItem get() {
|
||||
synchronized (lock) {
|
||||
if (pool == null)
|
||||
return new SymbolItem();
|
||||
|
||||
SymbolItem ti = pool;
|
||||
pool = pool.next;
|
||||
|
||||
ti.next = null;
|
||||
|
||||
return ti;
|
||||
}
|
||||
}
|
||||
|
||||
public static void release(SymbolItem ti) {
|
||||
if (ti == null)
|
||||
return;
|
||||
|
||||
synchronized (lock) {
|
||||
while (ti != null) {
|
||||
SymbolItem next = ti.next;
|
||||
|
||||
ti.drawable = null;
|
||||
ti.bitmap = null;
|
||||
|
||||
ti.next = pool;
|
||||
pool = ti;
|
||||
|
||||
ti = next;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SymbolItem next;
|
||||
|
||||
public Bitmap bitmap;
|
||||
public Drawable drawable;
|
||||
public float x;
|
||||
public float y;
|
||||
public boolean billboard;
|
||||
public int state;
|
||||
|
||||
// center, top, bottom, left, right, top-left...
|
||||
byte placement;
|
||||
// byte placement;
|
||||
|
||||
}
|
||||
|
||||
@ -20,34 +20,37 @@ import org.oscim.renderer.TextureObject;
|
||||
import org.oscim.renderer.TextureRenderer;
|
||||
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.util.Log;
|
||||
|
||||
// TODO share one static texture for all poi map symabols
|
||||
|
||||
public final class SymbolLayer extends TextureLayer {
|
||||
private static String TAG = SymbolLayer.class.getSimpleName();
|
||||
private final static String TAG = SymbolLayer.class.getSimpleName();
|
||||
|
||||
private final static int TEXTURE_WIDTH = TextureObject.TEXTURE_WIDTH;
|
||||
private final static int TEXTURE_HEIGHT = TextureObject.TEXTURE_HEIGHT;
|
||||
private final static float SCALE = 8.0f;
|
||||
|
||||
private static short[] mVertices;
|
||||
|
||||
SymbolItem symbols;
|
||||
|
||||
private Canvas mCanvas;
|
||||
private Rect mRect = new Rect();
|
||||
|
||||
public SymbolLayer() {
|
||||
if (mVertices == null)
|
||||
mVertices = new short[TextureRenderer.MAX_ITEMS * 24];
|
||||
type = Layer.SYMBOL;
|
||||
fixed = true;
|
||||
mCanvas = new Canvas();
|
||||
}
|
||||
|
||||
public void addSymbol(SymbolItem item) {
|
||||
|
||||
verticesCnt += 4;
|
||||
|
||||
SymbolItem it = symbols;
|
||||
|
||||
for (; it != null; it = it.next) {
|
||||
for (SymbolItem it = symbols; it != null; it = it.next) {
|
||||
if (it.bitmap == item.bitmap) {
|
||||
// insert after same bitmap
|
||||
item.next = it.next;
|
||||
it.next = item;
|
||||
return;
|
||||
@ -58,30 +61,77 @@ public final class SymbolLayer extends TextureLayer {
|
||||
symbols = item;
|
||||
}
|
||||
|
||||
public void addDrawable(Drawable drawable, int state, float x, float y) {
|
||||
|
||||
verticesCnt += 4;
|
||||
|
||||
SymbolItem item = SymbolItem.get();
|
||||
item.drawable = drawable;
|
||||
item.x = x;
|
||||
item.y = y;
|
||||
item.billboard = true;
|
||||
item.state = state;
|
||||
|
||||
for (SymbolItem it = symbols; it != null; it = it.next) {
|
||||
if (it.drawable == item.drawable && it.state == item.state) {
|
||||
// insert after same drawable
|
||||
item.next = it.next;
|
||||
it.next = item;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
item.next = symbols;
|
||||
symbols = item;
|
||||
}
|
||||
|
||||
@Override
|
||||
void compile(ShortBuffer sbuf) {
|
||||
if (TextureRenderer.debug)
|
||||
Log.d("...", "compile");
|
||||
|
||||
for (TextureObject to = textures; to != null; to = to.next)
|
||||
TextureObject.uploadTexture(to);
|
||||
}
|
||||
|
||||
private final static int LBIT_MASK = 0xfffffffe;
|
||||
|
||||
// TODO ... reuse texture when only symbol position changed
|
||||
@Override
|
||||
public void compile(ShortBuffer sbuf) {
|
||||
public boolean prepare() {
|
||||
|
||||
short numIndices = 0;
|
||||
short offsetIndices = 0;
|
||||
short curIndices = 0;
|
||||
|
||||
int pos = 0;
|
||||
short buf[] = mVertices;
|
||||
int bufLen = buf.length;
|
||||
curItem = VertexPool.get();
|
||||
pool = curItem;
|
||||
VertexPoolItem si = curItem;
|
||||
|
||||
int pos = si.used;
|
||||
short buf[] = si.vertices;
|
||||
|
||||
int advanceY = 0;
|
||||
float x = 0;
|
||||
float y = 0;
|
||||
|
||||
Canvas canvas = TextureObject.getCanvas();
|
||||
TextureObject to = TextureObject.get();
|
||||
textures = to;
|
||||
mCanvas.setBitmap(to.bitmap);
|
||||
|
||||
int maxIndices = TextureRenderer.MAX_ITEMS * TextureRenderer.INDICES_PER_SPRITE;
|
||||
|
||||
for (SymbolItem it = symbols; it != null;) {
|
||||
float width, height;
|
||||
|
||||
// add bitmap
|
||||
float width = it.bitmap.getWidth();
|
||||
float height = it.bitmap.getHeight();
|
||||
if (it.bitmap != null) {
|
||||
// add bitmap
|
||||
width = it.bitmap.getWidth();
|
||||
height = it.bitmap.getHeight();
|
||||
} else {
|
||||
width = it.drawable.getIntrinsicWidth();
|
||||
height = it.drawable.getIntrinsicHeight();
|
||||
}
|
||||
|
||||
if (height > advanceY)
|
||||
advanceY = (int) height;
|
||||
@ -91,43 +141,67 @@ public final class SymbolLayer extends TextureLayer {
|
||||
y += advanceY;
|
||||
advanceY = (int) (height + 0.5f);
|
||||
|
||||
if (y + height > TEXTURE_HEIGHT) {
|
||||
Log.d(TAG, "reached max symbols");
|
||||
|
||||
TextureObject to = TextureObject.uploadCanvas(offsetIndices, numIndices);
|
||||
offsetIndices = numIndices;
|
||||
|
||||
to.next = textures;
|
||||
textures = to;
|
||||
|
||||
sbuf.put(buf, 0, pos);
|
||||
pos = 0;
|
||||
|
||||
x = 0;
|
||||
y = 0;
|
||||
advanceY = (int) height;
|
||||
}
|
||||
}
|
||||
|
||||
canvas.drawBitmap(it.bitmap, x, y, null);
|
||||
if (y + height > TEXTURE_HEIGHT || curIndices == maxIndices) {
|
||||
Log.d(TAG, "reached max symbols: " + numIndices);
|
||||
|
||||
float hw = width / 2.0f;
|
||||
float hh = height / 2.0f;
|
||||
to.offset = offsetIndices;
|
||||
to.vertices = curIndices;
|
||||
|
||||
short x1 = (short) (SCALE * (-hw));
|
||||
short x2 = (short) (SCALE * (hw));
|
||||
short y1 = (short) (SCALE * (hh));
|
||||
short y2 = (short) (SCALE * (-hh));
|
||||
numIndices += curIndices;
|
||||
offsetIndices = numIndices;
|
||||
curIndices = 0;
|
||||
|
||||
to.next = TextureObject.get();
|
||||
to = to.next;
|
||||
|
||||
mCanvas.setBitmap(to.bitmap);
|
||||
|
||||
x = 0;
|
||||
y = 0;
|
||||
advanceY = (int) height;
|
||||
}
|
||||
|
||||
if (it.bitmap != null) {
|
||||
mCanvas.drawBitmap(it.bitmap, x, y, null);
|
||||
} else {
|
||||
it.drawable.copyBounds(mRect);
|
||||
it.drawable.setBounds((int) x, (int) y, (int) (x + width), (int) (y + height));
|
||||
it.drawable.draw(mCanvas);
|
||||
it.drawable.setBounds(mRect);
|
||||
}
|
||||
|
||||
short x1, y1, x2, y2;
|
||||
|
||||
if (it.bitmap != null) {
|
||||
float hw = width / 2.0f;
|
||||
float hh = height / 2.0f;
|
||||
x1 = (short) (SCALE * (-hw));
|
||||
x2 = (short) (SCALE * (hw));
|
||||
y1 = (short) (SCALE * (hh));
|
||||
y2 = (short) (SCALE * (-hh));
|
||||
} else {
|
||||
// use drawable offsets (for marker hotspot)
|
||||
x2 = (short) (SCALE * (mRect.left));
|
||||
y2 = (short) (SCALE * (mRect.top));
|
||||
x1 = (short) (SCALE * (mRect.right));
|
||||
y1 = (short) (SCALE * (mRect.bottom));
|
||||
|
||||
}
|
||||
|
||||
short u1 = (short) (SCALE * x);
|
||||
short v1 = (short) (SCALE * y);
|
||||
short u2 = (short) (SCALE * (x + width));
|
||||
short v2 = (short) (SCALE * (y + height));
|
||||
|
||||
// add symbol items referencing the same bitmap
|
||||
// add symbol items referencing the same bitmap / drawable
|
||||
for (SymbolItem it2 = it;; it2 = it2.next) {
|
||||
|
||||
if (it2 == null || it2.bitmap != it.bitmap) {
|
||||
if (it2 == null
|
||||
// || (curIndices == maxIndices)
|
||||
|| (it.drawable != null && it2.drawable != it.drawable)
|
||||
|| (it.bitmap != null && it2.bitmap != it.bitmap)) {
|
||||
it = it2;
|
||||
break;
|
||||
}
|
||||
@ -166,24 +240,41 @@ public final class SymbolLayer extends TextureLayer {
|
||||
buf[pos++] = v1;
|
||||
|
||||
// six elements used to draw the four vertices
|
||||
numIndices += 6;
|
||||
curIndices += TextureRenderer.INDICES_PER_SPRITE;
|
||||
|
||||
if (pos == VertexPoolItem.SIZE) {
|
||||
si.used = VertexPoolItem.SIZE;
|
||||
si = si.next = VertexPool.get();
|
||||
buf = si.vertices;
|
||||
pos = 0;
|
||||
}
|
||||
|
||||
// FIXME this does not work, need to draw bitmap on next
|
||||
// texture...
|
||||
if (pos == bufLen) {
|
||||
sbuf.put(buf, 0, pos);
|
||||
pos = 0;
|
||||
}
|
||||
// if (pos == bufLen) {
|
||||
// sbuf.put(buf, 0, pos);
|
||||
// pos = 0;
|
||||
// }
|
||||
|
||||
x += width;
|
||||
}
|
||||
}
|
||||
|
||||
TextureObject to = TextureObject.uploadCanvas(offsetIndices, numIndices);
|
||||
to.offset = offsetIndices;
|
||||
to.vertices = curIndices;
|
||||
|
||||
to.next = textures;
|
||||
textures = to;
|
||||
si.used = pos;
|
||||
curItem = si;
|
||||
|
||||
sbuf.put(buf, 0, pos);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void clear() {
|
||||
TextureObject.release(textures);
|
||||
SymbolItem.release(symbols);
|
||||
textures = null;
|
||||
symbols = null;
|
||||
verticesCnt = 0;
|
||||
}
|
||||
}
|
||||
|
||||
@ -27,7 +27,9 @@ public class TextItem {
|
||||
|
||||
TextItem ti = pool;
|
||||
pool = pool.next;
|
||||
|
||||
ti.next = null;
|
||||
|
||||
return ti;
|
||||
}
|
||||
}
|
||||
@ -73,5 +75,6 @@ public class TextItem {
|
||||
public Text text;
|
||||
public float width;
|
||||
public short x1, y1, x2, y2;
|
||||
|
||||
// public byte placement
|
||||
}
|
||||
|
||||
@ -25,26 +25,28 @@ import android.util.Log;
|
||||
|
||||
public final class TextLayer extends TextureLayer {
|
||||
|
||||
private static String TAG = TextureLayer.class.getSimpleName();
|
||||
// private static String TAG = TextureLayer.class.getSimpleName();
|
||||
|
||||
private final static int TEXTURE_WIDTH = TextureObject.TEXTURE_WIDTH;
|
||||
private final static int TEXTURE_HEIGHT = TextureObject.TEXTURE_HEIGHT;
|
||||
private final static float SCALE = 8.0f;
|
||||
private final static int LBIT_MASK = 0xfffffffe;
|
||||
|
||||
private static short[] mVertices;
|
||||
private static int mFontPadX = 1;
|
||||
private static int mFontPadY = 1;
|
||||
|
||||
TextItem labels;
|
||||
|
||||
private Canvas mCanvas;
|
||||
|
||||
public TextItem getLabels() {
|
||||
return labels;
|
||||
}
|
||||
|
||||
public TextLayer() {
|
||||
if (mVertices == null)
|
||||
mVertices = new short[TextureRenderer.MAX_ITEMS * 24];
|
||||
type = Layer.SYMBOL;
|
||||
mCanvas = new Canvas();
|
||||
fixed = true;
|
||||
}
|
||||
|
||||
public void addText(TextItem item) {
|
||||
@ -53,8 +55,10 @@ public final class TextLayer extends TextureLayer {
|
||||
|
||||
for (; it != null; it = it.next) {
|
||||
if (it.text == item.text) {
|
||||
// insert after text of same type
|
||||
item.next = it.next;
|
||||
it.next = item;
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
@ -64,24 +68,44 @@ public final class TextLayer extends TextureLayer {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void compile(ShortBuffer sbuf) {
|
||||
int numLabel = 0;
|
||||
void compile(ShortBuffer sbuf) {
|
||||
if (TextureRenderer.debug)
|
||||
Log.d("...", "compile");
|
||||
|
||||
for (TextureObject to = textures; to != null; to = to.next)
|
||||
TextureObject.uploadTexture(to);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean prepare() {
|
||||
if (TextureRenderer.debug)
|
||||
Log.d("...", "prepare");
|
||||
|
||||
// int numLabel = 0;
|
||||
// int numTextures = 0;
|
||||
|
||||
short numIndices = 0;
|
||||
short offsetIndices = 0;
|
||||
|
||||
int pos = 0;
|
||||
short buf[] = mVertices;
|
||||
int bufLen = buf.length;
|
||||
curItem = VertexPool.get();
|
||||
pool = curItem;
|
||||
|
||||
VertexPoolItem si = curItem;
|
||||
|
||||
int pos = si.used;
|
||||
short buf[] = si.vertices;
|
||||
|
||||
int advanceY = 0;
|
||||
float x = 0;
|
||||
float y = 0;
|
||||
float yy;
|
||||
|
||||
Canvas canvas = TextureObject.getCanvas();
|
||||
TextureObject to = TextureObject.get();
|
||||
textures = to;
|
||||
mCanvas.setBitmap(to.bitmap);
|
||||
|
||||
for (TextItem it = labels; it != null; it = it.next) {
|
||||
numLabel++;
|
||||
// numLabel++;
|
||||
|
||||
float width = it.width + 2 * mFontPadX;
|
||||
float height = (int) (it.text.fontHeight) + 2 * mFontPadY + 0.5f;
|
||||
@ -99,33 +123,27 @@ public final class TextLayer extends TextureLayer {
|
||||
// numLabel + " "
|
||||
// + ((numIndices - offsetIndices) / 6));
|
||||
|
||||
// need to sync bitmap upload somehow???
|
||||
TextureObject to = TextureObject.uploadCanvas(offsetIndices, numIndices);
|
||||
to.offset = offsetIndices;
|
||||
to.vertices = (short) (numIndices - offsetIndices);
|
||||
offsetIndices = numIndices;
|
||||
|
||||
to.next = textures;
|
||||
textures = to;
|
||||
to.next = TextureObject.get();
|
||||
to = to.next;
|
||||
|
||||
sbuf.put(buf, 0, pos);
|
||||
pos = 0;
|
||||
mCanvas.setBitmap(to.bitmap);
|
||||
|
||||
x = 0;
|
||||
y = 0;
|
||||
advanceY = (int) height;
|
||||
|
||||
// clear bitmap, TODO rotate two canvas to reduce the chance
|
||||
// of having upload lock draing to the canvas?
|
||||
canvas = TextureObject.getCanvas();
|
||||
// numTextures++;
|
||||
}
|
||||
}
|
||||
|
||||
yy = y + (height - 1) - it.text.fontDescent - mFontPadY;
|
||||
|
||||
if (it.text.stroke != null)
|
||||
canvas.drawText(it.string, x + it.width / 2, yy, it.text.stroke);
|
||||
mCanvas.drawText(it.string, x + it.width / 2, yy, it.text.stroke);
|
||||
|
||||
canvas.drawText(it.string, x + it.width / 2, yy, it.text.paint);
|
||||
mCanvas.drawText(it.string, x + it.width / 2, yy, it.text.paint);
|
||||
|
||||
// FIXME !!!
|
||||
if (width > TEXTURE_WIDTH)
|
||||
@ -207,30 +225,41 @@ public final class TextLayer extends TextureLayer {
|
||||
// six indices to draw the four vertices
|
||||
numIndices += 6;
|
||||
|
||||
// FIXME this does not work, need to draw bitmap on next
|
||||
// texture...
|
||||
if (pos == bufLen) {
|
||||
Log.d(TAG, "--- reached max label per texture " + numLabel);
|
||||
sbuf.put(buf, 0, pos);
|
||||
if (pos == VertexPoolItem.SIZE) {
|
||||
si.used = VertexPoolItem.SIZE;
|
||||
si = si.next = VertexPool.get();
|
||||
buf = si.vertices;
|
||||
pos = 0;
|
||||
}
|
||||
|
||||
// FIXME this does not work, need to draw bitmap on next
|
||||
// texture...
|
||||
// if (numLabel == TextureRenderer.MAX_ITEMS) {
|
||||
// Log.d(TAG, "--- reached max label per texture " + numLabel);
|
||||
// sbuf.put(buf, 0, pos);
|
||||
// pos = 0;
|
||||
// }
|
||||
|
||||
x += width;
|
||||
}
|
||||
|
||||
TextureObject to = TextureObject.uploadCanvas(offsetIndices, numIndices);
|
||||
to.offset = offsetIndices;
|
||||
to.vertices = (short) (numIndices - offsetIndices);
|
||||
|
||||
to.next = textures;
|
||||
textures = to;
|
||||
|
||||
sbuf.put(buf, 0, pos);
|
||||
si.used = pos;
|
||||
curItem = si;
|
||||
|
||||
// Log.d(TAG, "added labels " + numTextures + " " + numLabel);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void clear() {
|
||||
TextureObject.release(textures);
|
||||
TextItem.release(labels);
|
||||
textures = null;
|
||||
labels = null;
|
||||
verticesCnt = 0;
|
||||
}
|
||||
}
|
||||
|
||||
@ -20,12 +20,13 @@ import org.oscim.renderer.TextureObject;
|
||||
|
||||
public abstract class TextureLayer extends Layer {
|
||||
public TextureObject textures;
|
||||
public boolean fixed;
|
||||
|
||||
/**
|
||||
* @param sbuf
|
||||
* buffer to add vertices
|
||||
*/
|
||||
void compile(ShortBuffer sbuf) {
|
||||
}
|
||||
abstract void compile(ShortBuffer sbuf);
|
||||
|
||||
abstract public boolean prepare();
|
||||
}
|
||||
|
||||
@ -15,15 +15,12 @@
|
||||
package org.oscim.renderer.layer;
|
||||
|
||||
public class VertexPoolItem {
|
||||
final short[] vertices;
|
||||
final short[] vertices = new short[SIZE];
|
||||
|
||||
int used;
|
||||
VertexPoolItem next;
|
||||
|
||||
VertexPoolItem() {
|
||||
vertices = new short[SIZE];
|
||||
used = 0;
|
||||
}
|
||||
|
||||
// must be multiple of 4 (expected in LineLayer/PolygonLayer)
|
||||
static final int SIZE = 256;
|
||||
// and 24 (Texture Layer)
|
||||
static final int SIZE = 360;
|
||||
}
|
||||
|
||||
@ -14,6 +14,7 @@
|
||||
*/
|
||||
package org.oscim.renderer.overlays;
|
||||
|
||||
import org.oscim.core.MapPosition;
|
||||
import org.oscim.core.Tile;
|
||||
import org.oscim.renderer.layer.Layer;
|
||||
import org.oscim.renderer.layer.LineLayer;
|
||||
@ -27,13 +28,13 @@ import android.graphics.Color;
|
||||
import android.graphics.Paint.Cap;
|
||||
import android.util.Log;
|
||||
|
||||
public class OverlayGrid extends Overlay {
|
||||
public class OverlayGrid extends RenderOverlay {
|
||||
|
||||
private float[] mPoints;
|
||||
private short[] mIndex;
|
||||
private Text mText;
|
||||
|
||||
OverlayGrid(MapView mapView) {
|
||||
public OverlayGrid(MapView mapView) {
|
||||
super(mapView);
|
||||
|
||||
int size = Tile.TILE_SIZE;
|
||||
@ -91,6 +92,8 @@ public class OverlayGrid extends Overlay {
|
||||
tl.addText(ti);
|
||||
}
|
||||
}
|
||||
tl.prepare();
|
||||
|
||||
layers.textureLayers = tl;
|
||||
}
|
||||
|
||||
@ -107,7 +110,7 @@ public class OverlayGrid extends Overlay {
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void update(boolean positionChanged, boolean tilesChanged) {
|
||||
public synchronized void update(MapPosition curPos, boolean positionChanged, boolean tilesChanged) {
|
||||
|
||||
updateMapPosition();
|
||||
|
||||
@ -134,6 +137,8 @@ public class OverlayGrid extends Overlay {
|
||||
ll.width = 1.5f;
|
||||
ll.addLine(mPoints, mIndex, false);
|
||||
|
||||
Log.d("...", "update labels");
|
||||
|
||||
addLabels(x, y, mCurZ);
|
||||
|
||||
newData = true;
|
||||
|
||||
@ -14,16 +14,16 @@
|
||||
*/
|
||||
package org.oscim.renderer.overlays;
|
||||
|
||||
import org.oscim.renderer.layer.Layer;
|
||||
import org.oscim.renderer.layer.LineLayer;
|
||||
import java.io.IOException;
|
||||
|
||||
import org.oscim.core.MapPosition;
|
||||
import org.oscim.renderer.layer.SymbolItem;
|
||||
import org.oscim.renderer.layer.SymbolLayer;
|
||||
import org.oscim.renderer.layer.TextItem;
|
||||
import org.oscim.theme.renderinstruction.Line;
|
||||
import org.oscim.theme.renderinstruction.BitmapUtils;
|
||||
import org.oscim.view.MapView;
|
||||
|
||||
import android.graphics.Color;
|
||||
import android.graphics.Paint.Cap;
|
||||
|
||||
public class OverlayTest extends Overlay {
|
||||
public class OverlayTest extends RenderOverlay {
|
||||
|
||||
TextItem labels;
|
||||
|
||||
@ -31,15 +31,16 @@ public class OverlayTest extends Overlay {
|
||||
|
||||
private boolean first = true;
|
||||
|
||||
OverlayTest(MapView mapView) {
|
||||
public OverlayTest(MapView mapView) {
|
||||
super(mapView);
|
||||
|
||||
LineLayer ll = (LineLayer) layers.getLayer(1, Layer.LINE);
|
||||
ll.line = new Line(Color.BLUE, 1.0f, Cap.BUTT);
|
||||
ll.width = 2;
|
||||
float[] points = { -100, -100, 100, -100, 100, 100, -100, 100, -100, -100 };
|
||||
short[] index = { (short) points.length };
|
||||
ll.addLine(points, index, false);
|
||||
// LineLayer ll = (LineLayer) layers.getLayer(1, Layer.LINE);
|
||||
// ll.line = new Line(Color.BLUE, 1.0f, Cap.BUTT);
|
||||
// ll.width = 2;
|
||||
// float[] points = { -100, -100, 100, -100, 100, 100, -100, 100, -100, -100 };
|
||||
// short[] index = { (short) points.length };
|
||||
// ll.addLine(points, index, false);
|
||||
|
||||
//
|
||||
// PolygonLayer pl = (PolygonLayer) layers.getLayer(0, Layer.POLYGON);
|
||||
// pl.area = new Area(Color.argb(128, 255, 0, 0));
|
||||
@ -53,30 +54,33 @@ public class OverlayTest extends Overlay {
|
||||
// short[] pindex = { (short) ppoints.length };
|
||||
// pl.addPolygon(ppoints, pindex);
|
||||
|
||||
// SymbolLayer sl = new SymbolLayer();
|
||||
// SymbolItem it = new SymbolItem();
|
||||
//
|
||||
// it.x = 0;
|
||||
// it.y = 0;
|
||||
// // billboard always faces camera
|
||||
// it.billboard = true;
|
||||
//
|
||||
// try {
|
||||
// it.bitmap = BitmapUtils.createBitmap("file:/sdcard/cheshire.png");
|
||||
// } catch (IOException e) {
|
||||
// // TODO Auto-generated catch block
|
||||
// e.printStackTrace();
|
||||
// }
|
||||
// sl.addSymbol(it);
|
||||
//
|
||||
// SymbolItem it2 = new SymbolItem();
|
||||
// it2.bitmap = it.bitmap;
|
||||
// it2.x = 0;
|
||||
// it2.y = 0;
|
||||
// // billboard always faces camera
|
||||
// it2.billboard = false;
|
||||
//
|
||||
// sl.addSymbol(it2);
|
||||
SymbolLayer sl = new SymbolLayer();
|
||||
SymbolItem it = new SymbolItem();
|
||||
|
||||
it.x = 0;
|
||||
it.y = 0;
|
||||
// billboard always faces camera
|
||||
it.billboard = true;
|
||||
|
||||
try {
|
||||
it.bitmap = BitmapUtils.createBitmap("file:/sdcard/cheshire.png");
|
||||
} catch (IOException e) {
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
}
|
||||
sl.addSymbol(it);
|
||||
|
||||
SymbolItem it2 = new SymbolItem();
|
||||
it2.bitmap = it.bitmap;
|
||||
it2.x = 0;
|
||||
it2.y = 0;
|
||||
// billboard always faces camera
|
||||
it2.billboard = false;
|
||||
|
||||
sl.addSymbol(it2);
|
||||
sl.fixed = false;
|
||||
|
||||
layers.textureLayers = sl;
|
||||
|
||||
// TextLayer tl = new TextLayer();
|
||||
// Text t = Text.createText(20, 2, Color.WHITE, Color.BLACK, false);
|
||||
@ -92,14 +96,16 @@ public class OverlayTest extends Overlay {
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void update(boolean positionChanged, boolean tilesChanged) {
|
||||
public synchronized void update(MapPosition curPos, boolean positionChanged, boolean tilesChanged) {
|
||||
// keep position constant (or update layer relative to new position)
|
||||
mMapView.getMapViewPosition().getMapPosition(mMapPosition, null);
|
||||
//mMapView.getMapViewPosition().getMapPosition(mMapPosition, null);
|
||||
|
||||
if (first) {
|
||||
// fix at initial position
|
||||
// mapView.getMapViewPosition().getMapPosition(mMapPosition, null);
|
||||
updateMapPosition();
|
||||
|
||||
first = false;
|
||||
((SymbolLayer) (layers.textureLayers)).prepare();
|
||||
|
||||
// pass layers to be uploaded and drawn to GL Thread
|
||||
// afterwards never modify 'layers' outside of this function!
|
||||
|
||||
@ -17,8 +17,8 @@ package org.oscim.renderer.overlays;
|
||||
|
||||
import org.oscim.core.MapPosition;
|
||||
import org.oscim.core.Tile;
|
||||
import org.oscim.renderer.TileManager;
|
||||
import org.oscim.renderer.MapTile;
|
||||
import org.oscim.renderer.TileManager;
|
||||
import org.oscim.renderer.Tiles;
|
||||
import org.oscim.renderer.layer.TextItem;
|
||||
import org.oscim.renderer.layer.TextLayer;
|
||||
@ -29,20 +29,23 @@ import org.oscim.view.MapView;
|
||||
import android.os.SystemClock;
|
||||
import android.util.FloatMath;
|
||||
|
||||
public class OverlayText extends Overlay {
|
||||
public class OverlayText extends RenderOverlay {
|
||||
|
||||
private Tiles tiles;
|
||||
private LabelThread mThread;
|
||||
/* package */boolean mRun;
|
||||
/* package */boolean mRerun;
|
||||
|
||||
private MapPosition mWorkPos;
|
||||
private TextLayer mWorkLayer;
|
||||
private TextLayer mNewLayer;
|
||||
|
||||
/* package */boolean mRun;
|
||||
/* package */boolean mRerun;
|
||||
|
||||
class LabelThread extends PausableThread {
|
||||
|
||||
@Override
|
||||
protected void doWork() {
|
||||
SystemClock.sleep(250);
|
||||
SystemClock.sleep(300);
|
||||
mRun = false;
|
||||
updateLabels();
|
||||
mMapView.redrawMap();
|
||||
@ -70,14 +73,29 @@ public class OverlayText extends Overlay {
|
||||
void updateLabels() {
|
||||
tiles = TileManager.getActiveTiles(tiles);
|
||||
|
||||
// Log.d("...", "relabel " + mRerun + " " + x + " " + y);
|
||||
if (tiles.cnt == 0)
|
||||
return;
|
||||
|
||||
mMapView.getMapViewPosition().getMapPosition(mWorkPos, null);
|
||||
|
||||
// TODO tiles might be from another zoomlevel than the current:
|
||||
TextLayer tl = mWorkLayer;
|
||||
|
||||
if (tl == null)
|
||||
tl = new TextLayer();
|
||||
|
||||
// tiles might be from another zoomlevel than the current:
|
||||
// this scales MapPosition to the zoomlevel of tiles...
|
||||
// TODO create a helper function in MapPosition
|
||||
int diff = tiles.tiles[0].zoomLevel - mWorkPos.zoomLevel;
|
||||
|
||||
if (diff > 1 || diff < -2) {
|
||||
synchronized (this) {
|
||||
mNewLayer = tl;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
float div = FastMath.pow(diff);
|
||||
|
||||
// fix map position to tile coordinates
|
||||
@ -89,14 +107,11 @@ public class OverlayText extends Overlay {
|
||||
mWorkPos.zoomLevel += diff;
|
||||
mWorkPos.scale = div;
|
||||
|
||||
// Log.d("...", "relabel " + mRerun + " " + x + " " + y);
|
||||
|
||||
TextLayer tl = new TextLayer();
|
||||
|
||||
float angle = (float) Math.toRadians(mWorkPos.angle);
|
||||
float cos = FloatMath.cos(angle);
|
||||
float sin = FloatMath.sin(angle);
|
||||
|
||||
// TODO more sophisticated placement :)
|
||||
for (int i = 0, n = tiles.cnt; i < n; i++) {
|
||||
MapTile t = tiles.tiles[i];
|
||||
if (!t.isVisible)
|
||||
@ -104,7 +119,6 @@ public class OverlayText extends Overlay {
|
||||
|
||||
int dx = (t.tileX - x) * Tile.TILE_SIZE;
|
||||
int dy = (t.tileY - y) * Tile.TILE_SIZE;
|
||||
// Log.d("...", "update tiles " + dx + " " + dy);
|
||||
|
||||
for (TextItem ti = t.labels; ti != null; ti = ti.next) {
|
||||
|
||||
@ -129,20 +143,27 @@ public class OverlayText extends Overlay {
|
||||
}
|
||||
}
|
||||
|
||||
// draw text to bitmaps and create vertices
|
||||
tl.prepare();
|
||||
|
||||
// everything synchronized?
|
||||
synchronized (this) {
|
||||
mWorkLayer = tl;
|
||||
mNewLayer = tl;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void update(boolean positionChanged, boolean tilesChanged) {
|
||||
public synchronized void update(MapPosition curPos, boolean positionChanged, boolean tilesChanged) {
|
||||
// Log.d("...", "update " + tilesChanged + " " + positionChanged);
|
||||
|
||||
if (mWorkLayer != null) {
|
||||
if (mNewLayer != null) {
|
||||
|
||||
// keep text layer, not recrating its canvas each time...
|
||||
mWorkLayer = (TextLayer) layers.textureLayers;
|
||||
layers.clear();
|
||||
layers.textureLayers = mWorkLayer;
|
||||
mWorkLayer = null;
|
||||
|
||||
layers.textureLayers = mNewLayer;
|
||||
mNewLayer = null;
|
||||
|
||||
// make the 'labeled' MapPosition current
|
||||
MapPosition tmp = mMapPosition;
|
||||
@ -154,7 +175,6 @@ public class OverlayText extends Overlay {
|
||||
}
|
||||
|
||||
if (tilesChanged || positionChanged) {
|
||||
|
||||
if (!mRun) {
|
||||
mRun = true;
|
||||
synchronized (mThread) {
|
||||
|
||||
@ -28,12 +28,14 @@ import org.oscim.view.MapView;
|
||||
|
||||
import android.opengl.GLES20;
|
||||
import android.opengl.Matrix;
|
||||
import android.util.Log;
|
||||
|
||||
public abstract class Overlay {
|
||||
public abstract class RenderOverlay {
|
||||
|
||||
protected final MapView mMapView;
|
||||
// keep the Position for which the Overlay is rendered
|
||||
protected MapPosition mMapPosition;
|
||||
|
||||
// current Layers to draw
|
||||
public final Layers layers;
|
||||
|
||||
// flag to set when data is ready for (re)compilation.
|
||||
@ -44,19 +46,14 @@ public abstract class Overlay {
|
||||
|
||||
public BufferObject vbo;
|
||||
|
||||
public Overlay(MapView mapView) {
|
||||
public RenderOverlay(MapView mapView) {
|
||||
mMapView = mapView;
|
||||
mMapPosition = new MapPosition();
|
||||
layers = new Layers();
|
||||
}
|
||||
|
||||
synchronized boolean onTouch(boolean down) {
|
||||
Log.d("...", "Overlay handle onTouch " + down);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* update mMapPosition
|
||||
* Utility: update mMapPosition
|
||||
*
|
||||
* @return true if position has changed
|
||||
*/
|
||||
@ -68,12 +65,13 @@ public abstract class Overlay {
|
||||
// use synchronized (this){} when updating 'layers' from another thread
|
||||
|
||||
/**
|
||||
* @param curPos TODO
|
||||
* @param positionChanged
|
||||
* true when MapPosition has changed
|
||||
* @param tilesChanged
|
||||
* true when loaded tiles changed
|
||||
*/
|
||||
public synchronized void update(boolean positionChanged, boolean tilesChanged) {
|
||||
public synchronized void update(MapPosition curPos, boolean positionChanged, boolean tilesChanged) {
|
||||
// // keep position constant (or update layer relative to new position)
|
||||
// mMapView.getMapViewPosition().getMapPosition(mMapPosition, null);
|
||||
//
|
||||
@ -110,23 +108,20 @@ public abstract class Overlay {
|
||||
// float scale = curPos.scale / div;
|
||||
|
||||
for (Layer l = layers.textureLayers; l != null;) {
|
||||
|
||||
l = TextureRenderer.draw(l, (mMapPosition.scale / pos.scale) * div, proj, mv,
|
||||
layers.texOffset);
|
||||
}
|
||||
}
|
||||
|
||||
private float setMatrix(MapPosition curPos, float[] matrix) {
|
||||
// TODO if oPos == curPos this could be simplified
|
||||
|
||||
MapPosition oPos = mMapPosition;
|
||||
|
||||
byte z = oPos.zoomLevel;
|
||||
// int diff = curPos.zoomLevel - z;
|
||||
float div = FastMath.pow(z - curPos.zoomLevel);
|
||||
// if (diff < 0)
|
||||
// div = (1 << -diff);
|
||||
// else if (diff > 0)
|
||||
// div = (1.0f / (1 << diff));
|
||||
|
||||
float div = FastMath.pow(z - curPos.zoomLevel);
|
||||
float x = (float) (oPos.x - curPos.x * div);
|
||||
float y = (float) (oPos.y - curPos.y * div);
|
||||
|
||||
@ -145,8 +140,8 @@ public abstract class Overlay {
|
||||
matrix[12] = x * scale;
|
||||
matrix[13] = y * scale;
|
||||
|
||||
// scale to current tile world coordinates
|
||||
scale = (curPos.scale / oPos.scale) / div;
|
||||
// scale to tile to world coordinates
|
||||
scale /= GLRenderer.COORD_MULTIPLIER;
|
||||
matrix[0] = scale;
|
||||
matrix[5] = scale;
|
||||
@ -33,8 +33,7 @@ public final class BitmapUtils {
|
||||
if (src.startsWith(PREFIX_JAR)) {
|
||||
String name = "/org/oscim/theme/osmarender/" + src.substring(PREFIX_JAR.length());
|
||||
|
||||
InputStream inputStream = BitmapUtils.class
|
||||
.getResourceAsStream(name);
|
||||
InputStream inputStream = BitmapUtils.class.getResourceAsStream(name);
|
||||
if (inputStream == null) {
|
||||
throw new FileNotFoundException("resource not found: " + src + " " + name);
|
||||
}
|
||||
|
||||
@ -20,7 +20,8 @@ package org.oscim.utils;
|
||||
*/
|
||||
public final class GeometryUtils {
|
||||
/**
|
||||
* Calculates the center of the minimum bounding rectangle for the given coordinates.
|
||||
* Calculates the center of the minimum bounding rectangle for the given
|
||||
* coordinates.
|
||||
*
|
||||
* @param coordinates
|
||||
* the coordinates for which calculation should be done.
|
||||
@ -55,14 +56,16 @@ public final class GeometryUtils {
|
||||
* @return true if the given way is closed, false otherwise.
|
||||
*/
|
||||
static boolean isClosedWay(float[] way) {
|
||||
return Float.compare(way[0], way[way.length - 2]) == 0 && Float.compare(way[1], way[way.length - 1]) == 0;
|
||||
return Float.compare(way[0], way[way.length - 2]) == 0
|
||||
&& Float.compare(way[1], way[way.length - 1]) == 0;
|
||||
}
|
||||
|
||||
private GeometryUtils() {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
|
||||
static boolean linesIntersect(double x1, double y1, double x2, double y2, double x3, double y3, double x4, double y4) {
|
||||
static boolean linesIntersect(double x1, double y1, double x2, double y2, double x3, double y3,
|
||||
double x4, double y4) {
|
||||
// Return false if either of the lines have zero length
|
||||
if (x1 == x2 && y1 == y2 || x3 == x4 && y3 == y4) {
|
||||
return false;
|
||||
@ -111,9 +114,11 @@ public final class GeometryUtils {
|
||||
// since p1-p2 is parallel with p3-p4
|
||||
if (collinearityTestForP3 == 0) {
|
||||
// The lines are collinear. Now check if they overlap.
|
||||
if (x1 >= x3 && x1 <= x4 || x1 <= x3 && x1 >= x4 || x2 >= x3 && x2 <= x4 || x2 <= x3 && x2 >= x4
|
||||
if (x1 >= x3 && x1 <= x4 || x1 <= x3 && x1 >= x4 || x2 >= x3 && x2 <= x4
|
||||
|| x2 <= x3 && x2 >= x4
|
||||
|| x3 >= x1 && x3 <= x2 || x3 <= x1 && x3 >= x2) {
|
||||
if (y1 >= y3 && y1 <= y4 || y1 <= y3 && y1 >= y4 || y2 >= y3 && y2 <= y4 || y2 <= y3 && y2 >= y4
|
||||
if (y1 >= y3 && y1 <= y4 || y1 <= y3 && y1 >= y4 || y2 >= y3 && y2 <= y4
|
||||
|| y2 <= y3 && y2 >= y4
|
||||
|| y3 >= y1 && y3 <= y2 || y3 <= y1 && y3 >= y2) {
|
||||
return true;
|
||||
}
|
||||
@ -124,7 +129,8 @@ public final class GeometryUtils {
|
||||
return true;
|
||||
}
|
||||
|
||||
static boolean doesIntersect(double l1x1, double l1y1, double l1x2, double l1y2, double l2x1, double l2y1,
|
||||
static boolean doesIntersect(double l1x1, double l1y1, double l1x2, double l1y2, double l2x1,
|
||||
double l2y1,
|
||||
double l2x2, double l2y2) {
|
||||
double denom = ((l2y2 - l2y1) * (l1x2 - l1x1)) - ((l2x2 - l2x1) * (l1y2 - l1y1));
|
||||
|
||||
@ -157,7 +163,8 @@ public final class GeometryUtils {
|
||||
* ...
|
||||
* @return ...
|
||||
*/
|
||||
public static boolean lineIntersect(int x1, int y1, int x2, int y2, int x3, int y3, int x4, int y4) {
|
||||
public static boolean lineIntersect(int x1, int y1, int x2, int y2, int x3, int y3, int x4,
|
||||
int y4) {
|
||||
double denom = (y4 - y3) * (x2 - x1) - (x4 - x3) * (y2 - y1);
|
||||
if (denom == 0.0) { // Lines are parallel.
|
||||
return false;
|
||||
@ -171,108 +178,4 @@ public final class GeometryUtils {
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// private static final int OUT_LEFT = 1;
|
||||
// private static final int OUT_TOP = 2;
|
||||
// private static final int OUT_RIGHT = 4;
|
||||
// private static final int OUT_BOTTOM = 8;
|
||||
//
|
||||
//
|
||||
// private static int outcode(double x, double y) {
|
||||
// /*
|
||||
// * Note on casts to double below. If the arithmetic of
|
||||
// * x+w or y+h is done in float, then some bits may be
|
||||
// * lost if the binary exponents of x/y and w/h are not
|
||||
// * similar. By converting to double before the addition
|
||||
// * we force the addition to be carried out in double to
|
||||
// * avoid rounding error in the comparison.
|
||||
// *
|
||||
// * See bug 4320890 for problems that this inaccuracy causes.
|
||||
// */
|
||||
// int out = 0;
|
||||
// if (this.width <= 0) {
|
||||
// out |= OUT_LEFT | OUT_RIGHT;
|
||||
// } else if (x < this.x) {
|
||||
// out |= OUT_LEFT;
|
||||
// } else if (x > this.x + (double) this.width) {
|
||||
// out |= OUT_RIGHT;
|
||||
// }
|
||||
// if (this.height <= 0) {
|
||||
// out |= OUT_TOP | OUT_BOTTOM;
|
||||
// } else if (y < this.y) {
|
||||
// out |= OUT_TOP;
|
||||
// } else if (y > this.y + (double) this.height) {
|
||||
// out |= OUT_BOTTOM;
|
||||
// }
|
||||
// return out;
|
||||
// }
|
||||
|
||||
// from http://shamimkhaliq.50megs.com/Java/lineclipper.htm
|
||||
// private static int outCodes(Point P)
|
||||
// {
|
||||
// int Code = 0;
|
||||
//
|
||||
// if(P.y > yTop) Code += 1; /* code for above */
|
||||
// else if(P.y < yBottom) Code += 2; /* code for below */
|
||||
//
|
||||
// if(P.x > xRight) Code += 4; /* code for right */
|
||||
// else if(P.x < xLeft) Code += 8; /* code for left */
|
||||
//
|
||||
// return Code;
|
||||
// }
|
||||
//
|
||||
// private static boolean rejectCheck(int outCode1, int outCode2)
|
||||
// {
|
||||
// if ((outCode1 & outCode2) != 0 ) return true;
|
||||
// return(false);
|
||||
// }
|
||||
//
|
||||
//
|
||||
// private static boolean acceptCheck(int outCode1, int outCode2)
|
||||
// {
|
||||
// if ( (outCode1 == 0) && (outCode2 == 0) ) return(true);
|
||||
// return(false);
|
||||
// }
|
||||
//
|
||||
// static boolean CohenSutherland2DClipper(Point P0,Point P1)
|
||||
// {
|
||||
// int outCode0,outCode1;
|
||||
// while(true)
|
||||
// {
|
||||
// outCode0 = outCodes(P0);
|
||||
// outCode1 = outCodes(P1);
|
||||
// if( rejectCheck(outCode0,outCode1) ) return(false);
|
||||
// if( acceptCheck(outCode0,outCode1) ) return(true);
|
||||
// if(outCode0 == 0)
|
||||
// {
|
||||
// double tempCoord; int tempCode;
|
||||
// tempCoord = P0.x; P0.x= P1.x; P1.x = tempCoord;
|
||||
// tempCoord = P0.y; P0.y= P1.y; P1.y = tempCoord;
|
||||
// tempCode = outCode0; outCode0 = outCode1; outCode1 = tempCode;
|
||||
// }
|
||||
// if( (outCode0 & 1) != 0 )
|
||||
// {
|
||||
// P0.x += (P1.x - P0.x)*(yTop - P0.y)/(P1.y - P0.y);
|
||||
// P0.y = yTop;
|
||||
// }
|
||||
// else
|
||||
// if( (outCode0 & 2) != 0 )
|
||||
// {
|
||||
// P0.x += (P1.x - P0.x)*(yBottom - P0.y)/(P1.y - P0.y);
|
||||
// P0.y = yBottom;
|
||||
// }
|
||||
// else
|
||||
// if( (outCode0 & 4) != 0 )
|
||||
// {
|
||||
// P0.y += (P1.y - P0.y)*(xRight - P0.x)/(P1.x - P0.x);
|
||||
// P0.x = xRight;
|
||||
// }
|
||||
// else
|
||||
// if( (outCode0 & 8) != 0 )
|
||||
// {
|
||||
// P0.y += (P1.y - P0.y)*(xLeft - P0.x)/(P1.x - P0.x);
|
||||
// P0.x = xLeft;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
118
src/org/oscim/utils/LineClipper.java
Normal file
@ -0,0 +1,118 @@
|
||||
/*
|
||||
* Copyright 2012 Hannes Janetzek
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it under the
|
||||
* terms of the GNU Lesser 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 Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.oscim.utils;
|
||||
|
||||
import android.graphics.Point;
|
||||
|
||||
// from http://en.wikipedia.org/wiki/Cohen%E2%80%93Sutherland_algorithm
|
||||
|
||||
public class LineClipper {
|
||||
|
||||
private static final int INSIDE = 0; // 0000
|
||||
private static int LEFT = 1; // 0001
|
||||
private static int RIGHT = 2; // 0010
|
||||
private static int BOTTOM = 4; // 0100
|
||||
private static int TOP = 8; // 1000
|
||||
|
||||
// Compute the bit code for a point (x, y) using the clip rectangle
|
||||
// bounded diagonally by (xmin, ymin), and (xmax, ymax)
|
||||
|
||||
private int xmin, xmax, ymin, ymax;
|
||||
|
||||
public boolean clip(Point p1, Point p2, int minx, int miny, int maxx, int maxy) {
|
||||
this.xmin = minx;
|
||||
this.ymin = miny;
|
||||
this.xmax = maxx;
|
||||
this.ymax = maxy;
|
||||
|
||||
return cohenSutherlandLineClip(p1.x, p1.y, p2.x, p2.y);
|
||||
}
|
||||
|
||||
private int outCode(int x, int y) {
|
||||
int code;
|
||||
|
||||
code = INSIDE; // initialised as being inside of clip window
|
||||
|
||||
if (x < xmin) // to the left of clip window
|
||||
code |= LEFT;
|
||||
else if (x > xmax) // to the right of clip window
|
||||
code |= RIGHT;
|
||||
if (y < ymin) // below the clip window
|
||||
code |= BOTTOM;
|
||||
else if (y > ymax) // above the clip window
|
||||
code |= TOP;
|
||||
|
||||
return code;
|
||||
}
|
||||
|
||||
// CohenSutherland clipping algorithm clips a line from
|
||||
// P0 = (x0, y0) to P1 = (x1, y1) against a rectangle with
|
||||
// diagonal from (xmin, ymin) to (xmax, ymax).
|
||||
private boolean cohenSutherlandLineClip(int x0, int y0, int x1, int y1) {
|
||||
// compute outcodes for P0, P1, and whatever point lies outside the clip rectangle
|
||||
int outcode0 = outCode(x0, y0);
|
||||
int outcode1 = outCode(x1, y1);
|
||||
boolean accept = false;
|
||||
|
||||
while (true) {
|
||||
if ((outcode0 | outcode1) == 0) { // Bitwise OR is 0. Trivially accept and get out of loop
|
||||
accept = true;
|
||||
break;
|
||||
} else if ((outcode0 & outcode1) != 0) { // Bitwise AND is not 0. Trivially reject and get out of loop
|
||||
break;
|
||||
} else {
|
||||
// failed both tests, so calculate the line segment to clip
|
||||
// from an outside point to an intersection with clip edge
|
||||
int x = 0;
|
||||
int y = 0;
|
||||
|
||||
// At least one endpoint is outside the clip rectangle; pick it.
|
||||
int outcodeOut = (outcode0 == 0) ? outcode1 : outcode0;
|
||||
|
||||
// Now find the intersection point;
|
||||
// use formulas y = y0 + slope * (x - x0), x = x0 + (1 / slope) * (y - y0)
|
||||
if ((outcodeOut & TOP) != 0) { // point is above the clip rectangle
|
||||
x = x0 + (x1 - x0) * (ymax - y0) / (y1 - y0);
|
||||
y = ymax;
|
||||
} else if ((outcodeOut & BOTTOM) != 0) { // point is below the clip rectangle
|
||||
x = x0 + (x1 - x0) * (ymin - y0) / (y1 - y0);
|
||||
y = ymin;
|
||||
} else if ((outcodeOut & RIGHT) != 0) { // point is to the right of clip rectangle
|
||||
y = y0 + (y1 - y0) * (xmax - x0) / (x1 - x0);
|
||||
x = xmax;
|
||||
} else if ((outcodeOut & LEFT) != 0) { // point is to the left of clip rectangle
|
||||
y = y0 + (y1 - y0) * (xmin - x0) / (x1 - x0);
|
||||
x = xmin;
|
||||
}
|
||||
|
||||
// Now we move outside point to intersection point to clip
|
||||
// and get ready for next pass.
|
||||
if (outcodeOut == outcode0) {
|
||||
x0 = x;
|
||||
y0 = y;
|
||||
outcode0 = outCode(x0, y0);
|
||||
} else {
|
||||
x1 = x;
|
||||
y1 = y;
|
||||
outcode1 = outCode(x1, y1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO do sth with the result x0...
|
||||
return accept;
|
||||
}
|
||||
|
||||
}
|
||||
@ -19,10 +19,12 @@ import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.xml.parsers.ParserConfigurationException;
|
||||
|
||||
import org.oscim.core.BoundingBox;
|
||||
import org.oscim.core.GeoPoint;
|
||||
import org.oscim.core.MapPosition;
|
||||
import org.oscim.core.Tile;
|
||||
@ -34,6 +36,9 @@ import org.oscim.database.OpenResult;
|
||||
import org.oscim.generator.JobQueue;
|
||||
import org.oscim.generator.JobTile;
|
||||
import org.oscim.generator.MapWorker;
|
||||
import org.oscim.overlay.LabelingOverlay;
|
||||
import org.oscim.overlay.Overlay;
|
||||
import org.oscim.overlay.OverlayManager;
|
||||
import org.oscim.renderer.GLRenderer;
|
||||
import org.oscim.renderer.GLView;
|
||||
import org.oscim.renderer.TileGenerator;
|
||||
@ -43,29 +48,28 @@ import org.oscim.theme.InternalRenderTheme;
|
||||
import org.oscim.theme.RenderTheme;
|
||||
import org.oscim.theme.RenderThemeHandler;
|
||||
import org.oscim.theme.Theme;
|
||||
import org.oscim.utils.AndroidUtils;
|
||||
import org.xml.sax.SAXException;
|
||||
|
||||
import android.content.Context;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.Log;
|
||||
import android.view.MotionEvent;
|
||||
import android.widget.FrameLayout;
|
||||
import android.widget.RelativeLayout;
|
||||
|
||||
/**
|
||||
* A MapView shows a map on the display of the device. It handles all user input
|
||||
* and touch gestures to move and zoom the map.
|
||||
*/
|
||||
public class MapView extends FrameLayout {
|
||||
public class MapView extends RelativeLayout {
|
||||
|
||||
final static String TAG = "MapView";
|
||||
|
||||
public static final boolean debugFrameTime = false;
|
||||
public static final boolean testRegionZoom = false;
|
||||
// public static final boolean staticLabeling = false;
|
||||
|
||||
private static final boolean debugDatabase = false;
|
||||
|
||||
RegionLookup mRegionLookup;
|
||||
// RegionLookup mRegionLookup;
|
||||
|
||||
public boolean enableRotation = false;
|
||||
public boolean enableCompass = false;
|
||||
@ -81,10 +85,15 @@ public class MapView extends FrameLayout {
|
||||
private MapDatabases mMapDatabaseType;
|
||||
|
||||
private TileManager mTileManager;
|
||||
private GLView mGLView;
|
||||
private JobQueue mJobQueue;
|
||||
private MapWorker mMapWorkers[];
|
||||
private int mNumMapWorkers = 4;
|
||||
private final OverlayManager mOverlayManager;
|
||||
|
||||
private final GLView mGLView;
|
||||
private final JobQueue mJobQueue;
|
||||
|
||||
// TODO use 1 download and 1 generator thread instead
|
||||
private final MapWorker mMapWorkers[];
|
||||
private final int mNumMapWorkers = 4;
|
||||
|
||||
private DebugSettings debugSettings;
|
||||
private String mRenderTheme;
|
||||
private Map<String, String> mMapOptions;
|
||||
@ -124,8 +133,10 @@ public class MapView extends FrameLayout {
|
||||
}
|
||||
|
||||
Log.d(TAG, "create MapView: " + mapDatabaseType.name());
|
||||
// this.setDrawingCacheEnabled(true);
|
||||
this.setWillNotDraw(true);
|
||||
|
||||
// TODO make this dpi dependent
|
||||
// TODO set tilesize, make this dpi dependent
|
||||
Tile.TILE_SIZE = 400;
|
||||
|
||||
MapActivity mapActivity = (MapActivity) context;
|
||||
@ -138,6 +149,8 @@ public class MapView extends FrameLayout {
|
||||
|
||||
mMapViewPosition = new MapViewPosition(this);
|
||||
|
||||
mOverlayManager = new OverlayManager();
|
||||
|
||||
mTouchEventHandler = new TouchHandler(mapActivity, this);
|
||||
|
||||
mCompass = new Compass(mapActivity, this);
|
||||
@ -145,6 +158,7 @@ public class MapView extends FrameLayout {
|
||||
mJobQueue = new JobQueue();
|
||||
|
||||
mTileManager = TileManager.create(this);
|
||||
|
||||
mGLView = new GLView(context, this);
|
||||
|
||||
mMapWorkers = new MapWorker[mNumMapWorkers];
|
||||
@ -156,7 +170,7 @@ public class MapView extends FrameLayout {
|
||||
// .createMapDatabase(MapDatabases.TEST_READER);
|
||||
mapDatabase = MapDatabaseFactory
|
||||
.createMapDatabase(MapDatabases.MAP_READER);
|
||||
mNumMapWorkers = 1;
|
||||
// mNumMapWorkers = 1;
|
||||
} else {
|
||||
mapDatabase = MapDatabaseFactory.createMapDatabase(mapDatabaseType);
|
||||
}
|
||||
@ -187,8 +201,8 @@ public class MapView extends FrameLayout {
|
||||
|
||||
addView(mGLView, params);
|
||||
|
||||
if (testRegionZoom)
|
||||
mRegionLookup = new RegionLookup(this);
|
||||
// if (testRegionZoom)
|
||||
// mRegionLookup = new RegionLookup(this);
|
||||
|
||||
mMapZoomControls = new MapZoomControls(mapActivity, this);
|
||||
mMapZoomControls.setShowMapZoomControls(true);
|
||||
@ -197,6 +211,35 @@ public class MapView extends FrameLayout {
|
||||
|
||||
for (MapWorker worker : mMapWorkers)
|
||||
worker.start();
|
||||
|
||||
mOverlayManager.add(new LabelingOverlay(this));
|
||||
// mOverlayManager.add(new GenericOverlay(this, new OverlayGrid(this)));
|
||||
// mOverlayManager.add(new GenericOverlay(this, new OverlayTest(this)));
|
||||
|
||||
// ArrayList<OverlayItem> pList = new ArrayList<OverlayItem>();
|
||||
// pList.add(new OverlayItem("title", "description", new GeoPoint(53.067221, 8.78767)));
|
||||
// Overlay overlay = new ItemizedIconOverlay<OverlayItem>(this, context, pList, null);
|
||||
// mOverlayManager.add(overlay);
|
||||
|
||||
// ArrayList<OverlayItem> pList = new ArrayList<OverlayItem>();
|
||||
// pList.add(new ExtendedOverlayItem("Bremen", "description",
|
||||
// new GeoPoint(53.047221, 8.78767), context));
|
||||
// pList.add(new ExtendedOverlayItem("New York", "description",
|
||||
// new GeoPoint(40.4251, -74.021), context));
|
||||
// pList.add(new ExtendedOverlayItem("Tokyo", "description",
|
||||
// new GeoPoint(35.4122, 139.4130), context));
|
||||
// Overlay overlay = new ItemizedOverlayWithBubble<OverlayItem>(this, context, pList, null);
|
||||
// mOverlayManager.add(overlay);
|
||||
|
||||
// PathOverlay pathOverlay = new PathOverlay(this, Color.BLUE, context);
|
||||
// pathOverlay.addGreatCircle(
|
||||
// new GeoPoint(53.047221, 8.78767),
|
||||
// new GeoPoint(40.4251, -74.021));
|
||||
// // pathOverlay.addPoint(new GeoPoint(53.047221, 8.78767));
|
||||
// // pathOverlay.addPoint(new GeoPoint(53.067221, 8.78767));
|
||||
// mOverlayManager.add(pathOverlay);
|
||||
|
||||
mMapViewPosition.animateTo(new GeoPoint(53.067221, 8.78767));
|
||||
}
|
||||
|
||||
public void render() {
|
||||
@ -259,6 +302,9 @@ public class MapView extends FrameLayout {
|
||||
if (mPausing || this.getWidth() == 0 || this.getHeight() == 0)
|
||||
return;
|
||||
|
||||
if (AndroidUtils.currentThreadIsUiThread())
|
||||
mOverlayManager.onUpdate(mMapViewPosition.getMapPosition());
|
||||
|
||||
mTileManager.updateMap(false);
|
||||
}
|
||||
|
||||
@ -266,7 +312,8 @@ public class MapView extends FrameLayout {
|
||||
if (mPausing || this.getWidth() == 0 || this.getHeight() == 0)
|
||||
return;
|
||||
|
||||
mTileManager.updateMap(true);
|
||||
if (AndroidUtils.currentThreadIsUiThread())
|
||||
mTileManager.updateMap(true);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -291,7 +338,6 @@ public class MapView extends FrameLayout {
|
||||
|
||||
/**
|
||||
* Sets the map file for this MapView.
|
||||
*
|
||||
* @param mapOptions
|
||||
* ...
|
||||
* @return true if the map file was set correctly, false otherwise.
|
||||
@ -356,7 +402,6 @@ public class MapView extends FrameLayout {
|
||||
|
||||
/**
|
||||
* Sets the MapDatabase for this MapView.
|
||||
*
|
||||
* @param mapDatabaseType
|
||||
* the new MapDatabase.
|
||||
*/
|
||||
@ -396,7 +441,6 @@ public class MapView extends FrameLayout {
|
||||
|
||||
/**
|
||||
* Sets the internal theme which is used for rendering the map.
|
||||
*
|
||||
* @param internalRenderTheme
|
||||
* the internal rendering theme.
|
||||
* @return ...
|
||||
@ -418,7 +462,6 @@ public class MapView extends FrameLayout {
|
||||
|
||||
/**
|
||||
* Sets the theme file which is used for rendering the map.
|
||||
*
|
||||
* @param renderThemePath
|
||||
* the path to the XML file which defines the rendering theme.
|
||||
* @throws IllegalArgumentException
|
||||
@ -488,11 +531,11 @@ public class MapView extends FrameLayout {
|
||||
mapWorkersProceed();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
|
||||
super.onLayout(changed, left, top, right, bottom);
|
||||
mMapZoomControls.onLayout(changed, left, top, right, bottom);
|
||||
}
|
||||
// @Override
|
||||
// protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
|
||||
// // super.onLayout(changed, left, top, right, bottom);
|
||||
// mMapZoomControls.onLayout(changed, left, top, right, bottom);
|
||||
// }
|
||||
|
||||
void destroy() {
|
||||
for (MapWorker mapWorker : mMapWorkers) {
|
||||
@ -577,7 +620,6 @@ public class MapView extends FrameLayout {
|
||||
|
||||
/**
|
||||
* Sets the center and zoom level of this MapView and triggers a redraw.
|
||||
*
|
||||
* @param mapPosition
|
||||
* the new map position of this MapView.
|
||||
*/
|
||||
@ -591,7 +633,6 @@ public class MapView extends FrameLayout {
|
||||
|
||||
/**
|
||||
* Sets the center of the MapView and triggers a redraw.
|
||||
*
|
||||
* @param geoPoint
|
||||
* the new center point of the map.
|
||||
*/
|
||||
@ -611,7 +652,6 @@ public class MapView extends FrameLayout {
|
||||
|
||||
/**
|
||||
* add jobs and remember MapWorkers that stuff needs to be done
|
||||
*
|
||||
* @param jobs
|
||||
* tile jobs
|
||||
*/
|
||||
@ -648,6 +688,35 @@ public class MapView extends FrameLayout {
|
||||
mapWorker.proceed();
|
||||
}
|
||||
|
||||
/**
|
||||
* You can add/remove/reorder your Overlays using the List of
|
||||
* {@link Overlay}. The first (index 0) Overlay gets drawn first, the one
|
||||
* with the highest as the last one.
|
||||
* @return ...
|
||||
*/
|
||||
public List<Overlay> getOverlays() {
|
||||
return this.getOverlayManager();
|
||||
}
|
||||
|
||||
public OverlayManager getOverlayManager() {
|
||||
return mOverlayManager;
|
||||
}
|
||||
|
||||
public BoundingBox getBoundingBox() {
|
||||
return mMapViewPosition.getViewBox();
|
||||
}
|
||||
|
||||
// @Override
|
||||
// protected void onLayout(boolean changed, int l, int t, int r, int b) {
|
||||
// // TODO Auto-generated method stub
|
||||
//
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// protected void onMeasure()) {
|
||||
// // TODO Auto-generated method stub
|
||||
//
|
||||
// }
|
||||
// /**
|
||||
// * Sets the visibility of the zoom controls.
|
||||
// *
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
/*
|
||||
* Copyright 2010, 2011, 2012 mapsforge.org, 2012 Hannes Janetzek
|
||||
* Copyright 2010, 2011, 2012 mapsforge.org
|
||||
* Copyright 2012 Hannes Janetzek
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it under the
|
||||
* terms of the GNU Lesser General Public License as published by the Free Software
|
||||
@ -14,30 +15,35 @@
|
||||
*/
|
||||
package org.oscim.view;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
|
||||
import org.oscim.core.BoundingBox;
|
||||
import org.oscim.core.GeoPoint;
|
||||
import org.oscim.core.MapPosition;
|
||||
import org.oscim.core.MercatorProjection;
|
||||
import org.oscim.utils.FastMath;
|
||||
|
||||
import android.graphics.Point;
|
||||
import android.opengl.Matrix;
|
||||
import android.os.Handler;
|
||||
import android.os.Message;
|
||||
import android.os.SystemClock;
|
||||
import android.util.FloatMath;
|
||||
import android.util.Log;
|
||||
|
||||
/**
|
||||
* A MapPosition stores the latitude and longitude coordinate of a MapView
|
||||
* together with its zoom level.
|
||||
* together with its zoom level, rotation and tilt
|
||||
*/
|
||||
|
||||
// TODO use global coordinates that directly scale to pixel
|
||||
|
||||
public class MapViewPosition {
|
||||
|
||||
private static final String TAG = "MapViewPosition";
|
||||
private static final String TAG = MapViewPosition.class.getSimpleName();
|
||||
|
||||
public final static int MAX_ZOOMLEVEL = 17;
|
||||
public final static int MIN_ZOOMLEVEL = 2;
|
||||
|
||||
private final static float MAX_ANGLE = 35;
|
||||
private final static float MAX_ANGLE = 40;
|
||||
|
||||
private final MapView mMapView;
|
||||
|
||||
@ -51,6 +57,12 @@ public class MapViewPosition {
|
||||
private float mRotation;
|
||||
public float mTilt;
|
||||
|
||||
// private static final int REF_ZOOM = 20;
|
||||
private double mPosX;
|
||||
private double mPosY;
|
||||
|
||||
private AnimationHandler mHandler;
|
||||
|
||||
MapViewPosition(MapView mapView) {
|
||||
mMapView = mapView;
|
||||
mLatitude = Double.NaN;
|
||||
@ -60,6 +72,8 @@ public class MapViewPosition {
|
||||
mRotation = 0.0f;
|
||||
mTilt = 0;
|
||||
mMapScale = 1;
|
||||
|
||||
mHandler = new AnimationHandler(this);
|
||||
}
|
||||
|
||||
private float[] mProjMatrix = new float[16];
|
||||
@ -69,19 +83,12 @@ public class MapViewPosition {
|
||||
private float[] mRotMatrix = new float[16];
|
||||
private float[] mTmpMatrix = new float[16];
|
||||
|
||||
private int mHeight, mWidth;
|
||||
private static int mHeight, mWidth;
|
||||
public final static float VIEW_SCALE = 1f / 2;
|
||||
public final static float VIEW_DISTANCE = 2;
|
||||
public final static float VIEW_NEAR = VIEW_DISTANCE;
|
||||
public final static float VIEW_FAR = VIEW_DISTANCE * 2;
|
||||
|
||||
public final static float DIST = 2;
|
||||
|
||||
// public final static float VIEW_SCALE = 1f / 2;
|
||||
// public final static float VIEW_DISTANCE = 1;
|
||||
// public final static float VIEW_NEAR = 1;
|
||||
// public final static float VIEW_FAR = 2;
|
||||
|
||||
void setViewport(int width, int height) {
|
||||
float sw = VIEW_SCALE;
|
||||
float sh = VIEW_SCALE;
|
||||
@ -124,50 +131,211 @@ public class MapViewPosition {
|
||||
mapPosition.scale = mScale;
|
||||
mapPosition.zoomLevel = z;
|
||||
|
||||
mapPosition.x = MercatorProjection.longitudeToPixelX(mLongitude, z);
|
||||
mapPosition.y = MercatorProjection.latitudeToPixelY(mLatitude, z);
|
||||
mapPosition.x = mPosX;
|
||||
mapPosition.y = mPosY;
|
||||
|
||||
if (mapPosition.viewMatrix != null)
|
||||
System.arraycopy(mViewMatrix, 0, mapPosition.viewMatrix, 0, 16);
|
||||
|
||||
if (mapPosition.rotateMatrix != null)
|
||||
System.arraycopy(mRotMatrix, 0, mapPosition.rotateMatrix, 0, 16);
|
||||
|
||||
if (coords == null)
|
||||
return true;
|
||||
|
||||
// not so sure about this, but somehow works. weird z-values...
|
||||
float tilt = FloatMath.sin((float) Math.toRadians(mTilt
|
||||
// * 2.2f for dist = 1
|
||||
* 1.4f // for dist = 2
|
||||
// * 0.8f for dist = 4
|
||||
* ((float) mHeight / mWidth)));
|
||||
float tilt = getZ(1);
|
||||
|
||||
float d = 1f;
|
||||
unproject(-d, d, tilt, coords, 0); // bottom-left
|
||||
unproject(d, d, tilt, coords, 2); // bottom-right
|
||||
unproject(d, -d, -tilt, coords, 4); // top-right
|
||||
unproject(-d, -d, -tilt, coords, 6); // top-left
|
||||
unproject(-1, 1, tilt, coords, 0); // bottom-left
|
||||
unproject(1, 1, tilt, coords, 2); // bottom-right
|
||||
unproject(1, -1, -tilt, coords, 4); // top-right
|
||||
unproject(-1, -1, -tilt, coords, 6); // top-left
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/** @return the current center point of the MapView. */
|
||||
public synchronized GeoPoint getMapCenter() {
|
||||
return new GeoPoint(mLatitude, mLongitude);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return a MapPosition or null, if this map position is not valid.
|
||||
* @see #isValid()
|
||||
*/
|
||||
public synchronized MapPosition getMapPosition() {
|
||||
if (!isValid()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return new MapPosition(mLatitude, mLongitude, mZoomLevel, mScale, mRotation);
|
||||
}
|
||||
|
||||
/** @return the current zoom level of the MapView. */
|
||||
public synchronized byte getZoomLevel() {
|
||||
return mZoomLevel;
|
||||
}
|
||||
|
||||
/** @return the current scale of the MapView. */
|
||||
public synchronized float getScale() {
|
||||
return mScale;
|
||||
}
|
||||
|
||||
/**
|
||||
* ...
|
||||
* @return BoundingBox containing view
|
||||
*/
|
||||
public synchronized BoundingBox getViewBox() {
|
||||
|
||||
float[] coords = mBBoxCoords;
|
||||
|
||||
float tilt = getZ(1);
|
||||
unproject(-1, 1, -tilt, coords, 0); // top-left
|
||||
unproject(1, 1, -tilt, coords, 2); // top-right
|
||||
unproject(1, -1, tilt, coords, 4); // bottom-right
|
||||
unproject(-1, -1, tilt, coords, 6); // bottom-left
|
||||
|
||||
byte z = mZoomLevel;
|
||||
double dx, dy;
|
||||
double minLat = 0, minLon = 0, maxLat = 0, maxLon = 0, lon, lat;
|
||||
|
||||
for (int i = 0; i < 8; i += 2) {
|
||||
|
||||
dx = mPosX - coords[i + 0] / mScale;
|
||||
dy = mPosY - coords[i + 1] / mScale;
|
||||
|
||||
lon = MercatorProjection.pixelXToLongitude(dx, z);
|
||||
lat = MercatorProjection.pixelYToLatitude(dy, z);
|
||||
if (i == 0) {
|
||||
minLon = maxLon = lon;
|
||||
minLat = maxLat = lat;
|
||||
} else {
|
||||
if (lat > maxLat)
|
||||
maxLat = lat;
|
||||
else if (lat < minLat)
|
||||
minLat = lat;
|
||||
|
||||
if (lon > maxLon)
|
||||
maxLon = lon;
|
||||
else if (lon < minLon)
|
||||
minLon = lon;
|
||||
}
|
||||
}
|
||||
|
||||
return new BoundingBox(minLat, minLon, maxLat, maxLon);
|
||||
}
|
||||
|
||||
private float[] mv = { 0, 0, 0, 1 };
|
||||
// private float[] mu = { 0, 0, 0, 1 };
|
||||
private float[] mu = { 0, 0, 0, 1 };
|
||||
private float[] mBBoxCoords = new float[8];
|
||||
|
||||
private void unproject(float x, float y, float z, float[] coords, int position) {
|
||||
// mv[0] = x;
|
||||
// mv[1] = y;
|
||||
// mv[2] = z - 2f;
|
||||
// // mv[2] = 1f / (z - 2f);
|
||||
// mv[3] = 1;
|
||||
// Matrix.multiplyMV(mu, 0, mProjMatrix, 0, mv, 0);
|
||||
/* get the depth-value of the map for the current tilt, approximately.
|
||||
* needed to un-project a point on screen to the position on the map. not
|
||||
* so sure about this, but at least somehow works. */
|
||||
private float getZ(float y) {
|
||||
return FloatMath.sin((float) Math.toRadians(mTilt))
|
||||
// * 2.2f // for dist = 1
|
||||
* 1.3f // for dist = 2
|
||||
// * 0.8f // for dist = 4
|
||||
* ((float) mHeight / mWidth) * y;
|
||||
}
|
||||
|
||||
/**
|
||||
* for x,y in screen coordinates get the point on the map in map-tile
|
||||
* coordinates
|
||||
* @param x ...
|
||||
* @param y ...
|
||||
* @param reuse ...
|
||||
* @return ...
|
||||
*/
|
||||
public synchronized Point getScreenPointOnMap(float x, float y, Point reuse) {
|
||||
Point out = reuse == null ? new Point() : reuse;
|
||||
|
||||
float mx = ((mWidth / 2) - x) / (mWidth / 2);
|
||||
float my = ((mHeight / 2) - y) / (mHeight / 2);
|
||||
|
||||
unproject(-mx, my, getZ(my), mu, 0);
|
||||
|
||||
out.x = (int) (mPosX + mu[0] / mScale);
|
||||
out.y = (int) (mPosY + mu[1] / mScale);
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
/**
|
||||
* get the GeoPoint for x,y in screen coordinates
|
||||
* @param x screen pixel x
|
||||
* @param y screen pixel y
|
||||
* @return the corresponding GeoPoint
|
||||
*/
|
||||
public synchronized GeoPoint fromScreenPixels(float x, float y) {
|
||||
float mx = ((mWidth / 2) - x) / (mWidth / 2);
|
||||
float my = ((mHeight / 2) - y) / (mHeight / 2);
|
||||
|
||||
unproject(-mx, my, getZ(my), mu, 0);
|
||||
|
||||
double dx = mPosX + mu[0] / mScale;
|
||||
double dy = mPosY + mu[1] / mScale;
|
||||
|
||||
GeoPoint p = new GeoPoint(
|
||||
MercatorProjection.pixelYToLatitude(dy, mZoomLevel),
|
||||
MercatorProjection.pixelXToLongitude(dx, mZoomLevel));
|
||||
|
||||
// Log.d(">>>", "fromScreenPixels " + p);
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
/**
|
||||
* get the screen pixel for a GeoPoint
|
||||
* @param geoPoint ...
|
||||
* @param reuse ...
|
||||
* @return ...
|
||||
*/
|
||||
public synchronized Point project(GeoPoint geoPoint, Point reuse) {
|
||||
Point out = reuse == null ? new Point() : reuse;
|
||||
|
||||
double x = MercatorProjection.longitudeToPixelX(geoPoint.getLongitude(),
|
||||
mZoomLevel);
|
||||
double y = MercatorProjection.latitudeToPixelY(geoPoint.getLatitude(),
|
||||
mZoomLevel);
|
||||
|
||||
mv[0] = (float) (x - mPosX) * mScale;
|
||||
mv[1] = (float) (y - mPosY) * mScale;
|
||||
mv[2] = 0;
|
||||
mv[3] = 1;
|
||||
|
||||
Matrix.multiplyMV(mv, 0, mViewMatrix, 0, mv, 0);
|
||||
Matrix.multiplyMV(mv, 0, mProjMatrix, 0, mv, 0);
|
||||
|
||||
out.x = (int) (mv[0] / mv[3] * mWidth / 2);
|
||||
out.y = (int) (mv[1] / mv[3] * mHeight / 2);
|
||||
|
||||
// Log.d(">>>", "project: " + out.x + " " + out.y);
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
public synchronized void getMVP(float[] matrix) {
|
||||
Matrix.multiplyMM(matrix, 0, mProjMatrix, 0, mViewMatrix, 0);
|
||||
}
|
||||
|
||||
// public static Point project(float x, float y, float[] matrix, float[] tmpVec, Point reuse) {
|
||||
// Point out = reuse == null ? new Point() : reuse;
|
||||
//
|
||||
// tmpVec[0] = x;
|
||||
// tmpVec[1] = y;
|
||||
// tmpVec[2] = 0;
|
||||
// tmpVec[3] = 1;
|
||||
//
|
||||
// Matrix.multiplyMV(tmpVec, 0, matrix, 0, tmpVec, 0);
|
||||
//
|
||||
// out.x = (int) (tmpVec[0] / tmpVec[3] * mWidth / 2);
|
||||
// out.y = (int) (tmpVec[1] / tmpVec[3] * mHeight / 2);
|
||||
//
|
||||
// return out;
|
||||
// }
|
||||
|
||||
private void unproject(float x, float y, float z, float[] coords, int position) {
|
||||
mv[0] = x;
|
||||
mv[1] = y;
|
||||
mv[2] = z - 1f;
|
||||
// mv[2] = -mu[2] / mu[3];
|
||||
mv[3] = 1;
|
||||
|
||||
Matrix.multiplyMV(mv, 0, mUnprojMatrix, 0, mv, 0);
|
||||
@ -175,17 +343,13 @@ public class MapViewPosition {
|
||||
if (mv[3] != 0) {
|
||||
coords[position] = mv[0] / mv[3];
|
||||
coords[position + 1] = mv[1] / mv[3];
|
||||
// Log.d(TAG, (z * 1.4f - 1) + " " + mu[2] / mu[3] + " - " + x + ":"
|
||||
// + y + " - "
|
||||
// + coords[position] + ":" + coords[position + 1] + " - " + mTilt);
|
||||
} else {
|
||||
// else what?
|
||||
Log.d(TAG, "... what?");
|
||||
Log.d(TAG, "uproject failed");
|
||||
}
|
||||
}
|
||||
|
||||
private void updateMatrix() {
|
||||
Matrix.setRotateM(mRotMatrix, 0, mRotation, 0, 0, 1);
|
||||
// - view matrix
|
||||
// 1. scale to window coordinates
|
||||
// 2. rotate
|
||||
@ -195,11 +359,13 @@ public class MapViewPosition {
|
||||
// 4. translate to near-plane
|
||||
// 5. apply projection
|
||||
|
||||
Matrix.setRotateM(mRotMatrix, 0, mRotation, 0, 0, 1);
|
||||
|
||||
// tilt map
|
||||
float tilt = mTilt;
|
||||
Matrix.setRotateM(mTmpMatrix, 0, tilt, 1, 0, 0);
|
||||
|
||||
// apply first viewMatrix, then tilt
|
||||
// apply first rotation, then tilt
|
||||
Matrix.multiplyMM(mRotMatrix, 0, mTmpMatrix, 0, mRotMatrix, 0);
|
||||
|
||||
// scale to window coordinates
|
||||
@ -231,91 +397,7 @@ public class MapViewPosition {
|
||||
Matrix.multiplyMM(mUnprojMatrix, 0, mTmpMatrix, 0, mProjMatrixI, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* sets viewBox to visible bounding box, (left,top,right,bottom)
|
||||
*
|
||||
* @param viewBox
|
||||
* ...
|
||||
*/
|
||||
public synchronized void getViewBox(final float[] viewBox) {
|
||||
|
||||
// updateMatrix();
|
||||
|
||||
float tilt = FloatMath.sin((float) Math.toRadians(mTilt)) * 4;
|
||||
|
||||
unproject(-1, 1, -tilt, mBBoxCoords, 0); // top-left
|
||||
unproject(1, 1, -tilt, mBBoxCoords, 2); // top-right
|
||||
unproject(1, -1, tilt, mBBoxCoords, 4); // bottom-right
|
||||
unproject(-1, -1, tilt, mBBoxCoords, 6); // bottom-left
|
||||
|
||||
byte z = mZoomLevel;
|
||||
double pixelX = MercatorProjection.longitudeToPixelX(mLongitude, z);
|
||||
double pixelY = MercatorProjection.latitudeToPixelY(mLatitude, z);
|
||||
|
||||
double dx = pixelX - mBBoxCoords[0] / mScale;
|
||||
double dy = pixelY - mBBoxCoords[1] / mScale;
|
||||
double lon = MercatorProjection.pixelXToLongitude(dx, z);
|
||||
double lat = MercatorProjection.pixelYToLatitude(dy, z);
|
||||
Log.d(">>>", "bl:" + lon + " " + lat);
|
||||
|
||||
dx = pixelX - mBBoxCoords[2] / mScale;
|
||||
dy = pixelY - mBBoxCoords[3] / mScale;
|
||||
lon = MercatorProjection.pixelXToLongitude(dx, z);
|
||||
lat = MercatorProjection.pixelYToLatitude(dy, z);
|
||||
Log.d("...", "br:" + lon + " " + lat);
|
||||
|
||||
dx = pixelX - mBBoxCoords[4] / mScale;
|
||||
dy = pixelY - mBBoxCoords[5] / mScale;
|
||||
lon = MercatorProjection.pixelXToLongitude(dx, z);
|
||||
lat = MercatorProjection.pixelYToLatitude(dy, z);
|
||||
Log.d("...", "tl:" + lon + " " + lat);
|
||||
|
||||
dx = pixelX - mBBoxCoords[6] / mScale;
|
||||
dy = pixelY - mBBoxCoords[7] / mScale;
|
||||
lon = MercatorProjection.pixelXToLongitude(dx, z);
|
||||
lat = MercatorProjection.pixelYToLatitude(dy, z);
|
||||
Log.d("...", "tr:" + lon + " " + lat);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the current center point of the MapView.
|
||||
*/
|
||||
public synchronized GeoPoint getMapCenter() {
|
||||
return new GeoPoint(mLatitude, mLongitude);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return an immutable MapPosition or null, if this map position is not
|
||||
* valid.
|
||||
* @see #isValid()
|
||||
*/
|
||||
public synchronized MapPosition getMapPosition() {
|
||||
if (!isValid()) {
|
||||
return null;
|
||||
}
|
||||
// Log.d("MapViewPosition", "lat: " + mLatitude + " lon: " +
|
||||
// mLongitude);
|
||||
return new MapPosition(mLatitude, mLongitude, mZoomLevel, mScale, mRotation);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the current zoom level of the MapView.
|
||||
*/
|
||||
public synchronized byte getZoomLevel() {
|
||||
return mZoomLevel;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the current scale of the MapView.
|
||||
*/
|
||||
public synchronized float getScale() {
|
||||
return mScale;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true if this MapViewPosition is valid, false otherwise.
|
||||
*/
|
||||
/** @return true if this MapViewPosition is valid, false otherwise. */
|
||||
public synchronized boolean isValid() {
|
||||
if (Double.isNaN(mLatitude)) {
|
||||
return false;
|
||||
@ -336,168 +418,43 @@ public class MapViewPosition {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get GeoPoint for a pixel on screen
|
||||
*
|
||||
* @param x
|
||||
* ...
|
||||
* @param y
|
||||
* ...
|
||||
* @return the GeoPoint
|
||||
*/
|
||||
public GeoPoint getOffsetPoint(float x, float y) {
|
||||
double pixelX = MercatorProjection.longitudeToPixelX(mLongitude, mZoomLevel);
|
||||
double pixelY = MercatorProjection.latitudeToPixelY(mLatitude, mZoomLevel);
|
||||
|
||||
double dx = ((mMapView.getWidth() >> 1) - x) / mScale;
|
||||
double dy = ((mMapView.getHeight() >> 1) - y) / mScale;
|
||||
|
||||
if (mMapView.enableRotation || mMapView.enableCompass) {
|
||||
double rad = Math.toRadians(mRotation);
|
||||
double xx = dx * Math.cos(rad) + dy * -Math.sin(rad);
|
||||
double yy = dx * Math.sin(rad) + dy * Math.cos(rad);
|
||||
|
||||
dx = pixelX - xx;
|
||||
dy = pixelY - yy;
|
||||
} else {
|
||||
dx = pixelX - dx;
|
||||
dy = pixelY - dy;
|
||||
}
|
||||
|
||||
double latitude = MercatorProjection.pixelYToLatitude(dy, mZoomLevel);
|
||||
latitude = MercatorProjection.limitLatitude(latitude);
|
||||
|
||||
double longitude = MercatorProjection.pixelXToLongitude(dx, mZoomLevel);
|
||||
longitude = MercatorProjection.limitLongitude(longitude);
|
||||
|
||||
return new GeoPoint(latitude, longitude);
|
||||
}
|
||||
|
||||
// public static double pixelXToLongitude(double pixelX, byte zoomLevel) {
|
||||
// return 360 * ((pixelX / ((long) Tile.TILE_SIZE << zoomLevel)) - 0.5);
|
||||
// }
|
||||
//
|
||||
// public static double pixelYToLatitude(double pixelY, byte zoomLevel) {
|
||||
// double y = 0.5 - (pixelY / ((long) Tile.TILE_SIZE << zoomLevel));
|
||||
// return 90 - 360 * Math.atan(Math.exp(-y * (2 * Math.PI))) / Math.PI;
|
||||
// }
|
||||
|
||||
/**
|
||||
* Moves this MapViewPosition by the given amount of pixels.
|
||||
*
|
||||
* @param mx
|
||||
* the amount of pixels to move the map horizontally.
|
||||
* @param my
|
||||
* the amount of pixels to move the map vertically.
|
||||
* @param mx the amount of pixels to move the map horizontally.
|
||||
* @param my the amount of pixels to move the map vertically.
|
||||
*/
|
||||
public synchronized void moveMap(float mx, float my) {
|
||||
double pixelX = MercatorProjection.longitudeToPixelX(mLongitude, mZoomLevel);
|
||||
double pixelY = MercatorProjection.latitudeToPixelY(mLatitude, mZoomLevel);
|
||||
|
||||
double dx = mx / mScale;
|
||||
double dy = my / mScale;
|
||||
|
||||
if (mMapView.enableRotation || mMapView.enableCompass) {
|
||||
double rad = Math.toRadians(mRotation);
|
||||
double x = dx * Math.cos(rad) + dy * Math.sin(rad);
|
||||
double y = dx * -Math.sin(rad) + dy * Math.cos(rad);
|
||||
double rcos = Math.cos(rad);
|
||||
double rsin = Math.sin(rad);
|
||||
double x = dx * rcos + dy * rsin;
|
||||
double y = dx * -rsin + dy * rcos;
|
||||
dx = x;
|
||||
dy = y;
|
||||
}
|
||||
|
||||
dx = pixelX - dx;
|
||||
dy = pixelY - dy;
|
||||
dx = mPosX - dx;
|
||||
dy = mPosY - dy;
|
||||
|
||||
mLatitude = MercatorProjection.pixelYToLatitude(dy, mZoomLevel);
|
||||
mLatitude = MercatorProjection.limitLatitude(mLatitude);
|
||||
|
||||
mLongitude = MercatorProjection.pixelXToLongitude(dx, mZoomLevel);
|
||||
|
||||
mLongitude = MercatorProjection.wrapLongitude(mLongitude);
|
||||
// mLongitude = MercatorProjection.limitLongitude(mLongitude);
|
||||
|
||||
// getViewBox(null);
|
||||
updatePosition();
|
||||
}
|
||||
|
||||
public synchronized void rotateMap(float angle, float cx, float cy) {
|
||||
moveMap(cx, cy);
|
||||
// Log.d("MapViewPosition", "rotate:" + angle + " " + (mRotation -
|
||||
// angle));
|
||||
mRotation += angle;
|
||||
updateMatrix();
|
||||
}
|
||||
|
||||
public void setRotation(float f) {
|
||||
mRotation = f;
|
||||
updateMatrix();
|
||||
}
|
||||
|
||||
public boolean tilt(float move) {
|
||||
float tilt = mTilt + move;
|
||||
if (tilt > MAX_ANGLE)
|
||||
tilt = MAX_ANGLE;
|
||||
else if (tilt < 0)
|
||||
tilt = 0;
|
||||
|
||||
if (mTilt == tilt)
|
||||
return false;
|
||||
|
||||
setTilt(tilt);
|
||||
// mTilt = tilt;
|
||||
// updateMatrix();
|
||||
return true;
|
||||
}
|
||||
|
||||
public void setTilt(float f) {
|
||||
mTilt = f;
|
||||
|
||||
// float sw = VIEW_SCALE;
|
||||
// float sh = VIEW_SCALE;
|
||||
// sh += (mTilt / 250);
|
||||
|
||||
// Matrix.frustumM(mProjMatrix, 0, -sw * mWidth, sw * mWidth,
|
||||
// sh * mHeight, -sh * mHeight, 1, 2);
|
||||
//
|
||||
// Matrix.translateM(mProjMatrix, 0, 0, 0, -VIEW_DISTANCE);
|
||||
//
|
||||
// Matrix.invertM(mProjMatrixI, 0, mProjMatrix, 0);
|
||||
// Matrix.invertM(mUnprojMatrix, 0, mProjMatrix, 0);
|
||||
|
||||
updateMatrix();
|
||||
}
|
||||
|
||||
synchronized void setMapCenter(GeoPoint geoPoint) {
|
||||
mLatitude = MercatorProjection.limitLatitude(geoPoint.getLatitude());
|
||||
mLongitude = MercatorProjection.limitLongitude(geoPoint.getLongitude());
|
||||
}
|
||||
|
||||
synchronized void setMapCenter(MapPosition mapPosition) {
|
||||
mLatitude = MercatorProjection.limitLatitude(mapPosition.lat);
|
||||
mLongitude = MercatorProjection.limitLongitude(mapPosition.lon);
|
||||
mZoomLevel = mMapView.limitZoomLevel(mapPosition.zoomLevel);
|
||||
mMapScale = 1 << mZoomLevel;
|
||||
}
|
||||
|
||||
synchronized void setZoomLevel(byte zoomLevel) {
|
||||
mZoomLevel = mMapView.limitZoomLevel(zoomLevel);
|
||||
mMapScale = 1 << mZoomLevel;
|
||||
}
|
||||
|
||||
synchronized void setScale(float scale) {
|
||||
mScale = scale;
|
||||
}
|
||||
|
||||
// synchronized void zoomBoundingBox(GeoPoint p1, GeoPoint p2) {
|
||||
//
|
||||
// }
|
||||
|
||||
/**
|
||||
* @param scale
|
||||
* ...
|
||||
* @param pivotX
|
||||
* ...
|
||||
* @param pivotY
|
||||
* ...
|
||||
* -
|
||||
* @param scale ...
|
||||
* @param pivotX ...
|
||||
* @param pivotY ...
|
||||
* @return true if scale was changed
|
||||
*/
|
||||
public synchronized boolean scaleMap(float scale, float pivotX, float pivotY) {
|
||||
@ -512,13 +469,15 @@ public class MapViewPosition {
|
||||
if (z > MAX_ZOOMLEVEL) {
|
||||
// z17 shows everything, just increase scaling
|
||||
// need to fix this for ScanBox
|
||||
if (mScale * scale > 2) // 8)
|
||||
if (mScale * scale > 4)
|
||||
return false;
|
||||
|
||||
mScale *= scale;
|
||||
mMapScale = newScale;
|
||||
} else {
|
||||
mZoomLevel = (byte) z;
|
||||
updatePosition();
|
||||
|
||||
mScale = newScale / (1 << z);
|
||||
mMapScale = newScale;
|
||||
}
|
||||
@ -530,4 +489,182 @@ public class MapViewPosition {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* rotate map around pivot cx,cy
|
||||
* @param angle ...
|
||||
* @param cx ...
|
||||
* @param cy ...
|
||||
*/
|
||||
public synchronized void rotateMap(float angle, float cx, float cy) {
|
||||
moveMap(cx, cy);
|
||||
mRotation += angle;
|
||||
|
||||
updateMatrix();
|
||||
}
|
||||
|
||||
public synchronized void setRotation(float f) {
|
||||
mRotation = f;
|
||||
updateMatrix();
|
||||
}
|
||||
|
||||
public synchronized boolean tilt(float move) {
|
||||
float tilt = mTilt + move;
|
||||
if (tilt > MAX_ANGLE)
|
||||
tilt = MAX_ANGLE;
|
||||
else if (tilt < 0)
|
||||
tilt = 0;
|
||||
|
||||
if (mTilt == tilt)
|
||||
return false;
|
||||
|
||||
setTilt(tilt);
|
||||
return true;
|
||||
}
|
||||
|
||||
public synchronized void setTilt(float f) {
|
||||
mTilt = f;
|
||||
updateMatrix();
|
||||
}
|
||||
|
||||
private void setMapCenter(double latitude, double longitude) {
|
||||
mLatitude = MercatorProjection.limitLatitude(latitude);
|
||||
mLongitude = MercatorProjection.limitLongitude(longitude);
|
||||
updatePosition();
|
||||
}
|
||||
|
||||
synchronized void setMapCenter(GeoPoint geoPoint) {
|
||||
setMapCenter(geoPoint.getLatitude(), geoPoint.getLongitude());
|
||||
}
|
||||
|
||||
synchronized void setMapCenter(MapPosition mapPosition) {
|
||||
mZoomLevel = mMapView.limitZoomLevel(mapPosition.zoomLevel);
|
||||
mMapScale = 1 << mZoomLevel;
|
||||
setMapCenter(mapPosition.lat, mapPosition.lon);
|
||||
}
|
||||
|
||||
synchronized void setZoomLevel(byte zoomLevel) {
|
||||
mZoomLevel = mMapView.limitZoomLevel(zoomLevel);
|
||||
mMapScale = 1 << mZoomLevel;
|
||||
updatePosition();
|
||||
}
|
||||
|
||||
synchronized void setScale(float scale) {
|
||||
mScale = scale;
|
||||
}
|
||||
|
||||
private void updatePosition() {
|
||||
mPosX = MercatorProjection.longitudeToPixelX(mLongitude, mZoomLevel);
|
||||
mPosY = MercatorProjection.latitudeToPixelY(mLatitude, mZoomLevel);
|
||||
}
|
||||
|
||||
private double mStartX;
|
||||
private double mStartY;
|
||||
private double mEndX;
|
||||
private double mEndY;
|
||||
private float mDuration = 500;
|
||||
private Point mTmpPoint;
|
||||
|
||||
public synchronized void animateTo(GeoPoint geoPoint) {
|
||||
MercatorProjection.projectPoint(geoPoint, mZoomLevel, mTmpPoint);
|
||||
|
||||
mEndX = MercatorProjection.longitudeToPixelX(geoPoint.getLongitude(), mZoomLevel);
|
||||
mEndY = MercatorProjection.latitudeToPixelY(geoPoint.getLatitude(), mZoomLevel);
|
||||
mStartX = mPosX;
|
||||
mStartY = mPosY;
|
||||
|
||||
mDuration = 300;
|
||||
mHandler.start((int) mDuration);
|
||||
}
|
||||
|
||||
synchronized void setMapPosition(double x, double y) {
|
||||
|
||||
mLatitude = MercatorProjection.pixelYToLatitude(y, mZoomLevel);
|
||||
mLatitude = MercatorProjection.limitLatitude(mLatitude);
|
||||
|
||||
mLongitude = MercatorProjection.pixelXToLongitude(x, mZoomLevel);
|
||||
mLongitude = MercatorProjection.wrapLongitude(mLongitude);
|
||||
|
||||
updatePosition();
|
||||
}
|
||||
|
||||
void onTick(long millisLeft) {
|
||||
double adv = millisLeft / mDuration;
|
||||
double mx = (mStartX + (mEndX - mStartX) * (1.0 - adv));
|
||||
double my = (mStartY + (mEndY - mStartY) * (1.0 - adv));
|
||||
setMapPosition(mx, my);
|
||||
mMapView.redrawMap();
|
||||
}
|
||||
|
||||
void onFinish() {
|
||||
setMapPosition(mEndX, mEndY);
|
||||
mMapView.redrawMap();
|
||||
}
|
||||
|
||||
static class AnimationHandler extends Handler {
|
||||
private final WeakReference<MapViewPosition> mMapViewPosition;
|
||||
private static final int MSG = 1;
|
||||
|
||||
long mMillisInFuture;
|
||||
|
||||
long mInterval = 16;
|
||||
|
||||
long mStopTimeInFuture;
|
||||
|
||||
AnimationHandler(MapViewPosition mapAnimator) {
|
||||
mMapViewPosition = new WeakReference<MapViewPosition>(mapAnimator);
|
||||
}
|
||||
|
||||
public synchronized final void start(int millis) {
|
||||
mMillisInFuture = millis;
|
||||
MapViewPosition animator = mMapViewPosition.get();
|
||||
if (animator == null)
|
||||
return;
|
||||
|
||||
if (mMillisInFuture <= 0) {
|
||||
animator.onFinish();
|
||||
return;
|
||||
}
|
||||
|
||||
mStopTimeInFuture = SystemClock.elapsedRealtime() + mMillisInFuture;
|
||||
removeMessages(MSG);
|
||||
sendMessage(obtainMessage(MSG));
|
||||
}
|
||||
|
||||
public final void cancel() {
|
||||
removeMessages(MSG);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleMessage(Message msg) {
|
||||
MapViewPosition animator = mMapViewPosition.get();
|
||||
if (animator == null)
|
||||
return;
|
||||
|
||||
final long millisLeft = mStopTimeInFuture
|
||||
- SystemClock.elapsedRealtime();
|
||||
|
||||
if (millisLeft <= 0) {
|
||||
animator.onFinish();
|
||||
} else if (millisLeft < mInterval) {
|
||||
// no tick, just delay until done
|
||||
sendMessageDelayed(obtainMessage(MSG), millisLeft);
|
||||
} else {
|
||||
long lastTickStart = SystemClock.elapsedRealtime();
|
||||
animator.onTick(millisLeft);
|
||||
|
||||
// take into account user's onTick taking time to
|
||||
// execute
|
||||
long delay = lastTickStart + mInterval
|
||||
- SystemClock.elapsedRealtime();
|
||||
|
||||
// special case: user's onTick took more than interval
|
||||
// to
|
||||
// complete, skip to next interval
|
||||
while (delay < 0)
|
||||
delay += mInterval;
|
||||
|
||||
sendMessageDelayed(obtainMessage(MSG), delay);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010, 2011, 2012 mapsforge.org
|
||||
* Copyright 2012 Hannes Janetzek
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it under the
|
||||
* terms of the GNU Lesser General Public License as published by the Free Software
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
/*
|
||||
* Copyright 2010, 2011, 2012 mapsforge.org
|
||||
* Copyright 2012 Hannes Janetzek
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it under the
|
||||
* terms of the GNU Lesser General Public License as published by the Free Software
|
||||
@ -12,55 +13,52 @@
|
||||
* You should have received a copy of the GNU Lesser General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.oscim.view;
|
||||
|
||||
import org.oscim.core.Tile;
|
||||
import org.oscim.overlay.OverlayManager;
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.CountDownTimer;
|
||||
import android.os.Handler;
|
||||
import android.os.Message;
|
||||
import android.os.SystemClock;
|
||||
import android.util.Log;
|
||||
import android.view.GestureDetector;
|
||||
import android.view.GestureDetector.SimpleOnGestureListener;
|
||||
import android.view.GestureDetector.OnDoubleTapListener;
|
||||
import android.view.GestureDetector.OnGestureListener;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.ScaleGestureDetector;
|
||||
import android.view.ViewConfiguration;
|
||||
import android.view.ScaleGestureDetector.OnScaleGestureListener;
|
||||
import android.view.animation.DecelerateInterpolator;
|
||||
import android.widget.Scroller;
|
||||
|
||||
/**
|
||||
* Implementation for multi-touch capable devices.
|
||||
*/
|
||||
|
||||
// TODO:
|
||||
// - write a AnimationTimer instead of using CountDownTimers
|
||||
// - fix recognition of tilt/rotate/scale state...
|
||||
|
||||
final class TouchHandler
|
||||
extends SimpleOnGestureListener
|
||||
implements ScaleGestureDetector.OnScaleGestureListener {
|
||||
final class TouchHandler implements OnGestureListener, OnScaleGestureListener, OnDoubleTapListener {
|
||||
|
||||
private static final float SCALE_DURATION = 450;
|
||||
private static final String TAG = TouchHandler.class.getSimpleName();
|
||||
|
||||
private static final float SCALE_DURATION = 500;
|
||||
private static final float ROTATION_DELAY = 200; // ms
|
||||
|
||||
private static final int INVALID_POINTER_ID = -1;
|
||||
|
||||
private final MapView mMapView;
|
||||
private final MapViewPosition mMapPosition;
|
||||
private final DecelerateInterpolator mInterpolator = new DecelerateInterpolator();
|
||||
private final OverlayManager mOverlayManager;
|
||||
|
||||
private final DecelerateInterpolator mInterpolator;
|
||||
private final DecelerateInterpolator mLinearInterpolator;
|
||||
private boolean mBeginScale;
|
||||
private float mSumScale;
|
||||
|
||||
private final float mMapMoveDelta;
|
||||
private boolean mMoveStart;
|
||||
private boolean mBeginRotate;
|
||||
private boolean mBeginTilt;
|
||||
private boolean mLongPress;
|
||||
// private long mLongPressTime;
|
||||
|
||||
private float mPosX;
|
||||
// private float mPosX;
|
||||
private float mPosY;
|
||||
private double mAngle;
|
||||
|
||||
@ -76,16 +74,20 @@ final class TouchHandler
|
||||
* the MapView
|
||||
*/
|
||||
public TouchHandler(Context context, MapView mapView) {
|
||||
ViewConfiguration viewConfiguration = ViewConfiguration.get(context);
|
||||
mMapView = mapView;
|
||||
mMapPosition = mapView.getMapPosition();
|
||||
mMapMoveDelta = viewConfiguration.getScaledTouchSlop();
|
||||
mOverlayManager = mapView.getOverlayManager();
|
||||
// ViewConfiguration viewConfiguration = ViewConfiguration.get(context);
|
||||
// mMapMoveDelta = viewConfiguration.getScaledTouchSlop();
|
||||
mActivePointerId = INVALID_POINTER_ID;
|
||||
mScaleGestureDetector = new ScaleGestureDetector(context, this);
|
||||
mGestureDetector = new GestureDetector(context, this);
|
||||
mGestureDetector.setOnDoubleTapListener(this);
|
||||
|
||||
mScroller = new Scroller(mMapView.getContext(),
|
||||
new android.view.animation.LinearInterpolator());
|
||||
mInterpolator = new DecelerateInterpolator(1.5f);
|
||||
|
||||
mScroller = new Scroller(mMapView.getContext(), mInterpolator);
|
||||
mLinearInterpolator = new DecelerateInterpolator(0.8f);//new android.view.animation.LinearInterpolator();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -95,16 +97,8 @@ final class TouchHandler
|
||||
*/
|
||||
public boolean handleMotionEvent(MotionEvent event) {
|
||||
|
||||
// workaround for a bug in the ScaleGestureDetector, see Android issue
|
||||
// #12976
|
||||
// if (event.getAction() != MotionEvent.ACTION_MOVE
|
||||
// || event.getPointerCount() > 1) {
|
||||
|
||||
mGestureDetector.onTouchEvent(event);
|
||||
mScaleGestureDetector.onTouchEvent(event);
|
||||
// }
|
||||
|
||||
if (!mScaling)
|
||||
mGestureDetector.onTouchEvent(event);
|
||||
|
||||
int action = getAction(event);
|
||||
boolean ret = false;
|
||||
@ -136,10 +130,10 @@ final class TouchHandler
|
||||
}
|
||||
|
||||
private boolean onActionDown(MotionEvent event) {
|
||||
mPosX = event.getX();
|
||||
// mPosX = event.getX();
|
||||
mPosY = event.getY();
|
||||
|
||||
mMoveStart = false;
|
||||
// mMoveStart = false;
|
||||
mBeginRotate = false;
|
||||
mBeginTilt = false;
|
||||
// save the ID of the pointer
|
||||
@ -154,43 +148,19 @@ final class TouchHandler
|
||||
private boolean onActionMove(MotionEvent event) {
|
||||
int id = event.findPointerIndex(mActivePointerId);
|
||||
|
||||
// calculate the distance between previous and current position
|
||||
float moveX = event.getX(id) - mPosX;
|
||||
float moveY = event.getY(id) - mPosY;
|
||||
// save the position of the event
|
||||
|
||||
// Log.d("...", "mx " + moveX + " my " + moveY);
|
||||
|
||||
boolean scaling = mScaleGestureDetector.isInProgress();
|
||||
|
||||
if (!mScaling)
|
||||
mScaling = scaling;
|
||||
|
||||
if (!scaling && !mMoveStart) {
|
||||
|
||||
if (Math.abs(moveX) > mMapMoveDelta || Math.abs(moveY) > mMapMoveDelta) {
|
||||
// the map movement threshold has been reached
|
||||
// longPressDetector.pressStop();
|
||||
mMoveStart = true;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
mPosX = event.getX(id);
|
||||
mPosY = event.getY(id);
|
||||
float py = event.getY(id);
|
||||
float moveY = py - mPosY;
|
||||
mPosY = py;
|
||||
|
||||
// double-tap + hold
|
||||
if (mLongPress) {
|
||||
mMapPosition.scaleMap(1 - moveY / 100, 0, 0);
|
||||
mMapView.redrawMap();
|
||||
return true;
|
||||
}
|
||||
|
||||
if (multi == 0) {
|
||||
mMapPosition.moveMap(moveX, moveY);
|
||||
mMapView.redrawMap();
|
||||
if (multi == 0)
|
||||
return true;
|
||||
}
|
||||
|
||||
if (event.getEventTime() - mMultiTouchDownTime < ROTATION_DELAY)
|
||||
return true;
|
||||
@ -206,18 +176,22 @@ final class TouchHandler
|
||||
double rad = Math.atan2(dy, dx);
|
||||
double r = rad - mAngle;
|
||||
|
||||
if (!mBeginRotate) {
|
||||
if (!mBeginRotate && !mBeginScale) {
|
||||
/* our naive gesture detector for rotation and tilt.. */
|
||||
|
||||
if (Math.abs(rad) < 0.25 || Math.abs(rad) > Math.PI - 0.25) {
|
||||
mBeginTilt = true;
|
||||
if (mMapPosition.tilt(moveY / 4)) {
|
||||
mMapView.redrawMap();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!mBeginScale && !mBeginTilt) {
|
||||
if (!mBeginTilt) {
|
||||
if (Math.abs(r) > 0.05) {
|
||||
Log.d("...", "begin rotate");
|
||||
// Log.d(TAG, "begin rotate");
|
||||
mAngle = rad;
|
||||
mBeginRotate = true;
|
||||
}
|
||||
}
|
||||
@ -246,7 +220,7 @@ final class TouchHandler
|
||||
private long mMultiTouchDownTime;
|
||||
|
||||
private boolean onActionPointerDown(MotionEvent event) {
|
||||
// longPressDetector.pressStop();
|
||||
|
||||
mMultiTouchDownTime = event.getEventTime();
|
||||
|
||||
multi++;
|
||||
@ -256,7 +230,7 @@ final class TouchHandler
|
||||
double dy = event.getY(0) - event.getY(1);
|
||||
mAngle = Math.atan2(dy, dx);
|
||||
}
|
||||
Log.d("...", "multi down " + multi);
|
||||
// Log.d("...", "multi down " + multi);
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -274,14 +248,14 @@ final class TouchHandler
|
||||
pointerIndex = 0;
|
||||
}
|
||||
// save the position of the event
|
||||
mPosX = motionEvent.getX(pointerIndex);
|
||||
// mPosX = motionEvent.getX(pointerIndex);
|
||||
mPosY = motionEvent.getY(pointerIndex);
|
||||
mActivePointerId = motionEvent.getPointerId(pointerIndex);
|
||||
}
|
||||
multi--;
|
||||
|
||||
mLongPress = false;
|
||||
Log.d("...", "multi up " + multi);
|
||||
// Log.d("...", "multi up " + multi);
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -294,38 +268,32 @@ final class TouchHandler
|
||||
private boolean onActionUp(MotionEvent motionEvent) {
|
||||
mActivePointerId = INVALID_POINTER_ID;
|
||||
mScaling = false;
|
||||
multi = 0;
|
||||
|
||||
// if (mLongPress && SystemClock.uptimeMillis() - mLongPressTime < 150)
|
||||
// {
|
||||
// mScrollX = (mPosX - (mMapView.getWidth() >> 1)) * 2f;
|
||||
// mScrollY = (mPosY - (mMapView.getHeight() >> 1)) * 2f;
|
||||
// mPrevScale = 0;
|
||||
//
|
||||
// mTimer = new CountDownTimer((int) SCALE_DURATION, 30) {
|
||||
// @Override
|
||||
// public void onTick(long tick) {
|
||||
// scale2(tick);
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public void onFinish() {
|
||||
// scale2(0);
|
||||
// }
|
||||
// }.start();
|
||||
// }
|
||||
|
||||
mLongPress = false;
|
||||
|
||||
multi = 0;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/******************* SimpleOnGestureListener *******************/
|
||||
/******************* GestureListener *******************/
|
||||
|
||||
private Scroller mScroller;
|
||||
private float mScrollX, mScrollY;
|
||||
private boolean fling = false;
|
||||
|
||||
@Override
|
||||
public void onShowPress(MotionEvent e) {
|
||||
Log.d(TAG, "show press");
|
||||
// TODO Auto-generated method stub
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onSingleTapUp(MotionEvent e) {
|
||||
Log.d(TAG, "single tap up");
|
||||
return mOverlayManager.onSingleTapUp(e, mMapView);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onDown(MotionEvent e) {
|
||||
if (fling) {
|
||||
@ -338,6 +306,8 @@ final class TouchHandler
|
||||
fling = false;
|
||||
}
|
||||
|
||||
// Log.d(TAG, "tap");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -359,9 +329,32 @@ final class TouchHandler
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onScroll(final MotionEvent e1, final MotionEvent e2, final float distanceX,
|
||||
final float distanceY) {
|
||||
|
||||
if (mOverlayManager.onScroll(e1, e2, distanceX, distanceY, mMapView)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (mScaling)
|
||||
return true;
|
||||
|
||||
if (multi == 0) {
|
||||
mMapPosition.moveMap(-distanceX, -distanceY);
|
||||
mMapView.redrawMap();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,
|
||||
float velocityY) {
|
||||
|
||||
if (mScaling)
|
||||
return true;
|
||||
|
||||
int w = Tile.TILE_SIZE * 20;
|
||||
int h = Tile.TILE_SIZE * 20;
|
||||
mScrollX = 0;
|
||||
@ -376,7 +369,7 @@ final class TouchHandler
|
||||
-w, w, -h, h);
|
||||
|
||||
// animate for two seconds
|
||||
mTimer = new CountDownTimer(1500, 50) {
|
||||
mTimer = new CountDownTimer(1500, 16) {
|
||||
@Override
|
||||
public void onTick(long tick) {
|
||||
scroll();
|
||||
@ -384,20 +377,26 @@ final class TouchHandler
|
||||
|
||||
@Override
|
||||
public void onFinish() {
|
||||
// do nothing
|
||||
}
|
||||
}.start();
|
||||
fling = true;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLongPress(MotionEvent e) {
|
||||
if (MapView.testRegionZoom) {
|
||||
Log.d("mapsforge", "long press");
|
||||
mMapView.mRegionLookup.updateRegion(-1, null);
|
||||
if (mLongPress)
|
||||
return;
|
||||
|
||||
if (mOverlayManager.onLongPress(e, mMapView)) {
|
||||
return;
|
||||
}
|
||||
mLongPress = true;
|
||||
|
||||
// if (MapView.testRegionZoom) {
|
||||
// Log.d("mapsforge", "long press");
|
||||
// mMapView.mRegionLookup.updateRegion(-1, null);
|
||||
// }
|
||||
}
|
||||
|
||||
boolean scale2(long tick) {
|
||||
@ -422,36 +421,30 @@ final class TouchHandler
|
||||
return true;
|
||||
}
|
||||
|
||||
/******************* DoubleTapListener ****************/
|
||||
@Override
|
||||
public boolean onSingleTapConfirmed(MotionEvent e) {
|
||||
// Log.d(TAG, "single tap confirmed");
|
||||
return mOverlayManager.onSingleTapConfirmed(e, mMapView);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onDoubleTap(MotionEvent e) {
|
||||
if (MapView.testRegionZoom) {
|
||||
mMapView.mRegionLookup.updateRegion(1,
|
||||
mMapPosition.getOffsetPoint(mPosX, mPosY));
|
||||
} else {
|
||||
mLongPress = true;
|
||||
Log.d(TAG, "double tap");
|
||||
|
||||
// mLongPressTime = SystemClock.uptimeMillis();
|
||||
// mScrollX = (e.getX(0) - (mMapView.getWidth() >> 1)) * 2f;
|
||||
// mScrollY = (e.getY(0) - (mMapView.getHeight() >> 1)) * 2f;
|
||||
// mPrevScale = 0;
|
||||
//
|
||||
// mTimer = new CountDownTimer((int) SCALE_DURATION, 30) {
|
||||
// @Override
|
||||
// public void onTick(long tick) {
|
||||
// scale2(tick);
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public void onFinish() {
|
||||
// scale(0);
|
||||
// }
|
||||
// }.start();
|
||||
}
|
||||
return true;
|
||||
// TODO Auto-generated method stub
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onDoubleTapEvent(MotionEvent e) {
|
||||
mLongPress = true;
|
||||
Log.d(TAG, "double tap event");
|
||||
// TODO Auto-generated method stub
|
||||
return false;
|
||||
}
|
||||
|
||||
/******************* ScaleListener *******************/
|
||||
|
||||
private float mCenterX;
|
||||
private float mCenterY;
|
||||
private float mFocusX;
|
||||
@ -475,7 +468,7 @@ final class TouchHandler
|
||||
|
||||
if (!mBeginScale) {
|
||||
if (mSumScale > 1.1 || mSumScale < 0.9) {
|
||||
Log.d("...", "begin scale " + mSumScale);
|
||||
// Log.d("...", "begin scale " + mSumScale);
|
||||
mBeginScale = true;
|
||||
// scale = mSumScale;
|
||||
}
|
||||
@ -489,9 +482,11 @@ final class TouchHandler
|
||||
|
||||
@Override
|
||||
public boolean onScaleBegin(ScaleGestureDetector gd) {
|
||||
mScaling = true;
|
||||
mBeginScale = false;
|
||||
|
||||
mTimeEnd = mTimeStart = SystemClock.elapsedRealtime();
|
||||
mSumScale = 1;
|
||||
mBeginScale = false;
|
||||
mCenterX = mMapView.getWidth() >> 1;
|
||||
mCenterY = mMapView.getHeight() >> 1;
|
||||
|
||||
@ -514,19 +509,21 @@ final class TouchHandler
|
||||
|
||||
mZooutOut = mSumScale < 0.99;
|
||||
|
||||
mTimer = new CountDownTimer((int) SCALE_DURATION, 15) {
|
||||
mTimer = new CountDownTimer((int) SCALE_DURATION, 32) {
|
||||
@Override
|
||||
public void onTick(long tick) {
|
||||
scale(tick);
|
||||
scaleAnim(tick);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFinish() {
|
||||
scale(0);
|
||||
|
||||
scaleAnim(0);
|
||||
}
|
||||
}.start();
|
||||
} else {
|
||||
mScaling = false;
|
||||
}
|
||||
|
||||
mBeginScale = false;
|
||||
}
|
||||
|
||||
@ -534,7 +531,7 @@ final class TouchHandler
|
||||
private CountDownTimer mTimer;
|
||||
boolean mZooutOut;
|
||||
|
||||
boolean scale(long tick) {
|
||||
boolean scaleAnim(long tick) {
|
||||
|
||||
if (mPrevScale >= 1) {
|
||||
mTimer = null;
|
||||
@ -542,7 +539,8 @@ final class TouchHandler
|
||||
}
|
||||
|
||||
float adv = (SCALE_DURATION - tick) / SCALE_DURATION;
|
||||
adv = mInterpolator.getInterpolation(adv);
|
||||
// adv = mInterpolator.getInterpolation(adv);
|
||||
adv = mLinearInterpolator.getInterpolation(adv);
|
||||
|
||||
float scale = adv - mPrevScale;
|
||||
mPrevScale += scale;
|
||||
@ -560,120 +558,4 @@ final class TouchHandler
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* from CountDownTimer.java: Copyright (C) 2008 The Android Open Source
|
||||
* Project 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.
|
||||
*/
|
||||
|
||||
final static class Timer {
|
||||
|
||||
/**
|
||||
* Millis since epoch when alarm should stop.
|
||||
*/
|
||||
private final long mMillisInFuture;
|
||||
|
||||
/**
|
||||
* The interval in millis that the user receives callbacks
|
||||
*/
|
||||
final long mCountdownInterval;
|
||||
|
||||
long mStopTimeInFuture;
|
||||
|
||||
/**
|
||||
* @param millisInFuture
|
||||
* The number of millis in the future from the call to
|
||||
* {@link #start()} until the countdown is done and
|
||||
* {@link #onFinish()} is called.
|
||||
* @param countDownInterval
|
||||
* The interval along the way to receive
|
||||
* {@link #onTick(long)} callbacks.
|
||||
*/
|
||||
public Timer(long millisInFuture, long countDownInterval) {
|
||||
mMillisInFuture = millisInFuture;
|
||||
mCountdownInterval = countDownInterval;
|
||||
}
|
||||
|
||||
/**
|
||||
* Cancel the countdown.
|
||||
*/
|
||||
public final void cancel() {
|
||||
mHandler.removeMessages(MSG);
|
||||
}
|
||||
|
||||
/**
|
||||
* Start the countdown.
|
||||
*
|
||||
* @return ...
|
||||
*/
|
||||
public synchronized final Timer start() {
|
||||
if (mMillisInFuture <= 0) {
|
||||
onFinish();
|
||||
return this;
|
||||
}
|
||||
mStopTimeInFuture = SystemClock.elapsedRealtime() + mMillisInFuture;
|
||||
mHandler.sendMessage(mHandler.obtainMessage(MSG));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback fired on regular interval.
|
||||
*
|
||||
* @param millisUntilFinished
|
||||
* The amount of time until finished.
|
||||
*/
|
||||
public void onTick(long millisUntilFinished) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback fired when the time is up.
|
||||
*/
|
||||
public void onFinish() {
|
||||
}
|
||||
|
||||
private static final int MSG = 1;
|
||||
|
||||
// handles counting down
|
||||
private Handler mHandler = new Handler() {
|
||||
|
||||
@Override
|
||||
public void handleMessage(Message msg) {
|
||||
|
||||
synchronized (Timer.this) {
|
||||
final long millisLeft = mStopTimeInFuture
|
||||
- SystemClock.elapsedRealtime();
|
||||
|
||||
if (millisLeft <= 0) {
|
||||
onFinish();
|
||||
} else if (millisLeft < mCountdownInterval) {
|
||||
// no tick, just delay until done
|
||||
sendMessageDelayed(obtainMessage(MSG), millisLeft);
|
||||
} else {
|
||||
long lastTickStart = SystemClock.elapsedRealtime();
|
||||
onTick(millisLeft);
|
||||
|
||||
// take into account user's onTick taking time to
|
||||
// execute
|
||||
long delay = lastTickStart + mCountdownInterval
|
||||
- SystemClock.elapsedRealtime();
|
||||
|
||||
// special case: user's onTick took more than interval
|
||||
// to
|
||||
// complete, skip to next interval
|
||||
while (delay < 0)
|
||||
delay += mCountdownInterval;
|
||||
|
||||
sendMessageDelayed(obtainMessage(MSG), delay);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
135
src/org/osmdroid/location/FlickrPOIProvider.java
Normal file
@ -0,0 +1,135 @@
|
||||
package org.osmdroid.location;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
import org.oscim.core.BoundingBox;
|
||||
import org.oscim.core.GeoPoint;
|
||||
import org.osmdroid.utils.BonusPackHelper;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
/**
|
||||
* POI Provider using Flickr service to get geolocalized photos.
|
||||
* @see "http://www.flickr.com/services/api/flickr.photos.search.html"
|
||||
* @author M.Kergall
|
||||
*/
|
||||
public class FlickrPOIProvider {
|
||||
|
||||
protected String mApiKey;
|
||||
|
||||
/**
|
||||
* @param apiKey
|
||||
* the registered API key to give to Flickr service.
|
||||
* @see "http://www.flickr.com/help/api/"
|
||||
*/
|
||||
public FlickrPOIProvider(String apiKey) {
|
||||
mApiKey = apiKey;
|
||||
}
|
||||
|
||||
private String getUrlInside(BoundingBox boundingBox, int maxResults) {
|
||||
StringBuffer url = new StringBuffer(
|
||||
"http://api.flickr.com/services/rest/?method=flickr.photos.search");
|
||||
url.append("&api_key=" + mApiKey);
|
||||
url.append("&bbox=" + boundingBox.getMinLongitude());
|
||||
url.append("," + boundingBox.getMinLatitude());
|
||||
url.append("," + boundingBox.getMaxLongitude());
|
||||
url.append("," + boundingBox.getMaxLatitude());
|
||||
url.append("&has_geo=1");
|
||||
// url.append("&geo_context=2");
|
||||
// url.append("&is_commons=true");
|
||||
url.append("&format=json&nojsoncallback=1");
|
||||
url.append("&per_page=" + maxResults);
|
||||
// From Flickr doc:
|
||||
// "Geo queries require some sort of limiting agent in order to prevent the database from crying."
|
||||
// And min_date_upload is considered as a limiting agent. So:
|
||||
url.append("&min_upload_date=2005/01/01");
|
||||
|
||||
// Ask to provide some additional attributes we will need:
|
||||
url.append("&extras=geo,url_sq");
|
||||
url.append("&sort=interestingness-desc");
|
||||
return url.toString();
|
||||
}
|
||||
|
||||
/* public POI getPhoto(String photoId){ String url =
|
||||
* "http://api.flickr.com/services/rest/?method=flickr.photos.getInfo" +
|
||||
* "&api_key=" + mApiKey + "&photo_id=" + photo Id +
|
||||
* "&format=json&nojsoncallback=1"; Log.d(BonusPackHelper.LOG_TAG,
|
||||
* "getPhoto:"+url); String jString =
|
||||
* BonusPackHelper.requestStringFromUrl(url); if (jString == null) {
|
||||
* Log.e(BonusPackHelper.LOG_TAG, "FlickrPOIProvider: request failed.");
|
||||
* return null; } try { POI poi = new POI(POI.POI_SERVICE_FLICKR);
|
||||
* JSONObject jRoot = new JSONObject(jString); JSONObject jPhoto =
|
||||
* jRoot.getJSONObject("photo"); JSONObject jLocation =
|
||||
* jPhoto.getJSONObject("location"); poi.mLocation = new GeoPoint(
|
||||
* jLocation.getDouble("latitude"), jLocation.getDouble("longitude"));
|
||||
* poi.mId = Long.parseLong(photoId); JSONObject jTitle =
|
||||
* jPhoto.getJSONObject("title"); poi.mType = jTitle.getString("_content");
|
||||
* JSONObject jDescription = jPhoto.getJSONObject("description");
|
||||
* poi.mDescription = jDescription.getString("_content"); //truncate
|
||||
* description if too long: if (poi.mDescription.length() > 300){
|
||||
* poi.mDescription = poi.mDescription.substring(0, 300) + " (...)"; }
|
||||
* String farm = jPhoto.getString("farm"); String server =
|
||||
* jPhoto.getString("server"); String secret = jPhoto.getString("secret");
|
||||
* JSONObject jOwner = jPhoto.getJSONObject("owner"); String nsid =
|
||||
* jOwner.getString("nsid"); poi.mThumbnailPath =
|
||||
* "http://farm"+farm+".staticflickr.com/"
|
||||
* +server+"/"+photoId+"_"+secret+"_s.jpg"; poi.mUrl =
|
||||
* "http://www.flickr.com/photos/"+nsid+"/"+photoId; return poi; }catch
|
||||
* (JSONException e) { e.printStackTrace(); return null; } } */
|
||||
|
||||
/**
|
||||
* @param fullUrl ...
|
||||
* @return the list of POI
|
||||
*/
|
||||
public ArrayList<POI> getThem(String fullUrl) {
|
||||
// for local debug: fullUrl = "http://10.0.2.2/flickr_mockup.json";
|
||||
Log.d(BonusPackHelper.LOG_TAG, "FlickrPOIProvider:get:" + fullUrl);
|
||||
String jString = BonusPackHelper.requestStringFromUrl(fullUrl);
|
||||
if (jString == null) {
|
||||
Log.e(BonusPackHelper.LOG_TAG, "FlickrPOIProvider: request failed.");
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
JSONObject jRoot = new JSONObject(jString);
|
||||
JSONObject jPhotos = jRoot.getJSONObject("photos");
|
||||
JSONArray jPhotoArray = jPhotos.getJSONArray("photo");
|
||||
int n = jPhotoArray.length();
|
||||
ArrayList<POI> pois = new ArrayList<POI>(n);
|
||||
for (int i = 0; i < n; i++) {
|
||||
JSONObject jPhoto = jPhotoArray.getJSONObject(i);
|
||||
String photoId = jPhoto.getString("id");
|
||||
POI poi = new POI(POI.POI_SERVICE_FLICKR);
|
||||
poi.location = new GeoPoint(
|
||||
jPhoto.getDouble("latitude"),
|
||||
jPhoto.getDouble("longitude"));
|
||||
poi.id = Long.parseLong(photoId);
|
||||
poi.type = jPhoto.getString("title");
|
||||
poi.thumbnailPath = jPhoto.getString("url_sq");
|
||||
String owner = jPhoto.getString("owner");
|
||||
poi.url = "http://www.flickr.com/photos/" + owner + "/" + photoId;
|
||||
pois.add(poi);
|
||||
}
|
||||
int total = jPhotos.getInt("total");
|
||||
Log.d(BonusPackHelper.LOG_TAG, "done:" + n + " got, on a total of:" + total);
|
||||
return pois;
|
||||
} catch (JSONException e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param boundingBox ...
|
||||
* @param maxResults ...
|
||||
* @return list of POI, Flickr photos inside the bounding box. Null if
|
||||
* technical issue.
|
||||
*/
|
||||
public ArrayList<POI> getPOIInside(BoundingBox boundingBox, int maxResults) {
|
||||
String url = getUrlInside(boundingBox, maxResults);
|
||||
return getThem(url);
|
||||
}
|
||||
|
||||
}
|
||||
219
src/org/osmdroid/location/GeoNamesPOIProvider.java
Normal file
@ -0,0 +1,219 @@
|
||||
package org.osmdroid.location;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Locale;
|
||||
|
||||
import javax.xml.parsers.ParserConfigurationException;
|
||||
import javax.xml.parsers.SAXParser;
|
||||
import javax.xml.parsers.SAXParserFactory;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
import org.oscim.core.BoundingBox;
|
||||
import org.oscim.core.GeoPoint;
|
||||
import org.osmdroid.utils.BonusPackHelper;
|
||||
import org.osmdroid.utils.HttpConnection;
|
||||
import org.xml.sax.Attributes;
|
||||
import org.xml.sax.SAXException;
|
||||
import org.xml.sax.helpers.DefaultHandler;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
/**
|
||||
* POI Provider using GeoNames services. Currently, "find Nearby Wikipedia" and
|
||||
* "Wikipedia Articles in Bounding Box" services.
|
||||
* @see "http://www.geonames.org"
|
||||
* @author M.Kergall
|
||||
*/
|
||||
public class GeoNamesPOIProvider {
|
||||
|
||||
protected String mUserName;
|
||||
|
||||
/**
|
||||
* @param account
|
||||
* the registered "username" to give to GeoNames service.
|
||||
* @see "http://www.geonames.org/login"
|
||||
*/
|
||||
public GeoNamesPOIProvider(String account) {
|
||||
mUserName = account;
|
||||
}
|
||||
|
||||
private String getUrlCloseTo(GeoPoint p, int maxResults, double maxDistance) {
|
||||
StringBuffer url = new StringBuffer("http://api.geonames.org/findNearbyWikipediaJSON?");
|
||||
url.append("lat=" + p.getLatitude());
|
||||
url.append("&lng=" + p.getLongitude());
|
||||
url.append("&maxRows=" + maxResults);
|
||||
url.append("&radius=" + maxDistance); //km
|
||||
url.append("&lang=" + Locale.getDefault().getLanguage());
|
||||
url.append("&username=" + mUserName);
|
||||
return url.toString();
|
||||
}
|
||||
|
||||
private String getUrlInside(BoundingBox boundingBox, int maxResults) {
|
||||
StringBuffer url = new StringBuffer("http://api.geonames.org/wikipediaBoundingBoxJSON?");
|
||||
url.append("south=" + boundingBox.getMinLatitude());
|
||||
url.append("&north=" + boundingBox.getMaxLatitude());
|
||||
url.append("&west=" + boundingBox.getMinLongitude());
|
||||
url.append("&east=" + boundingBox.getMaxLongitude());
|
||||
url.append("&maxRows=" + maxResults);
|
||||
url.append("&lang=" + Locale.getDefault().getLanguage());
|
||||
url.append("&username=" + mUserName);
|
||||
return url.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param fullUrl
|
||||
* ...
|
||||
* @return the list of POI
|
||||
*/
|
||||
public ArrayList<POI> getThem(String fullUrl) {
|
||||
Log.d(BonusPackHelper.LOG_TAG, "GeoNamesPOIProvider:get:" + fullUrl);
|
||||
String jString = BonusPackHelper.requestStringFromUrl(fullUrl);
|
||||
if (jString == null) {
|
||||
Log.e(BonusPackHelper.LOG_TAG, "GeoNamesPOIProvider: request failed.");
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
JSONObject jRoot = new JSONObject(jString);
|
||||
JSONArray jPlaceIds = jRoot.getJSONArray("geonames");
|
||||
int n = jPlaceIds.length();
|
||||
ArrayList<POI> pois = new ArrayList<POI>(n);
|
||||
for (int i = 0; i < n; i++) {
|
||||
JSONObject jPlace = jPlaceIds.getJSONObject(i);
|
||||
POI poi = new POI(POI.POI_SERVICE_GEONAMES_WIKIPEDIA);
|
||||
poi.location = new GeoPoint(jPlace.getDouble("lat"),
|
||||
jPlace.getDouble("lng"));
|
||||
poi.category = jPlace.optString("feature");
|
||||
poi.type = jPlace.getString("title");
|
||||
poi.description = jPlace.optString("summary");
|
||||
poi.thumbnailPath = jPlace.optString("thumbnailImg", null);
|
||||
/* This makes loading too long. Thumbnail loading will be done
|
||||
* only when needed, with POI.getThumbnail() if
|
||||
* (poi.mThumbnailPath != null){ poi.mThumbnail =
|
||||
* BonusPackHelper.loadBitmap(poi.mThumbnailPath); } */
|
||||
poi.url = jPlace.optString("wikipediaUrl", null);
|
||||
if (poi.url != null)
|
||||
poi.url = "http://" + poi.url;
|
||||
poi.rank = jPlace.optInt("rank", 0);
|
||||
//other attributes: distance?
|
||||
pois.add(poi);
|
||||
}
|
||||
Log.d(BonusPackHelper.LOG_TAG, "done");
|
||||
return pois;
|
||||
} catch (JSONException e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
//XML parsing seems 2 times slower than JSON parsing
|
||||
public ArrayList<POI> getThemXML(String fullUrl) {
|
||||
Log.d(BonusPackHelper.LOG_TAG, "GeoNamesPOIProvider:get:" + fullUrl);
|
||||
HttpConnection connection = new HttpConnection();
|
||||
connection.doGet(fullUrl);
|
||||
InputStream stream = connection.getStream();
|
||||
if (stream == null) {
|
||||
return null;
|
||||
}
|
||||
GeoNamesXMLHandler handler = new GeoNamesXMLHandler();
|
||||
try {
|
||||
SAXParser parser = SAXParserFactory.newInstance().newSAXParser();
|
||||
parser.parse(stream, handler);
|
||||
} catch (ParserConfigurationException e) {
|
||||
e.printStackTrace();
|
||||
} catch (SAXException e) {
|
||||
e.printStackTrace();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
connection.close();
|
||||
Log.d(BonusPackHelper.LOG_TAG, "done");
|
||||
return handler.mPOIs;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param position
|
||||
* ...
|
||||
* @param maxResults
|
||||
* ...
|
||||
* @param maxDistance
|
||||
* ... in km. 20 km max for the free service.
|
||||
* @return list of POI, Wikipedia entries close to the position. Null if
|
||||
* technical issue.
|
||||
*/
|
||||
public ArrayList<POI> getPOICloseTo(GeoPoint position,
|
||||
int maxResults, double maxDistance) {
|
||||
String url = getUrlCloseTo(position, maxResults, maxDistance);
|
||||
return getThem(url);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param boundingBox
|
||||
* ...
|
||||
* @param maxResults
|
||||
* ...
|
||||
* @return list of POI, Wikipedia entries inside the bounding box. Null if
|
||||
* technical issue.
|
||||
*/
|
||||
public ArrayList<POI> getPOIInside(BoundingBox boundingBox, int maxResults) {
|
||||
String url = getUrlInside(boundingBox, maxResults);
|
||||
return getThem(url);
|
||||
}
|
||||
}
|
||||
|
||||
class GeoNamesXMLHandler extends DefaultHandler {
|
||||
|
||||
private String mString;
|
||||
double mLat, mLng;
|
||||
POI mPOI;
|
||||
ArrayList<POI> mPOIs;
|
||||
|
||||
public GeoNamesXMLHandler() {
|
||||
mPOIs = new ArrayList<POI>();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startElement(String uri, String localName, String name,
|
||||
Attributes attributes) {
|
||||
if (localName.equals("entry")) {
|
||||
mPOI = new POI(POI.POI_SERVICE_GEONAMES_WIKIPEDIA);
|
||||
}
|
||||
mString = new String();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void characters(char[] ch, int start, int length) {
|
||||
String chars = new String(ch, start, length);
|
||||
mString = mString.concat(chars);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void endElement(String uri, String localName, String name) {
|
||||
if (localName.equals("lat")) {
|
||||
mLat = Double.parseDouble(mString);
|
||||
} else if (localName.equals("lng")) {
|
||||
mLng = Double.parseDouble(mString);
|
||||
} else if (localName.equals("feature")) {
|
||||
mPOI.category = mString;
|
||||
} else if (localName.equals("title")) {
|
||||
mPOI.type = mString;
|
||||
} else if (localName.equals("summary")) {
|
||||
mPOI.description = mString;
|
||||
} else if (localName.equals("thumbnailImg")) {
|
||||
if (mString != null && !mString.equals(""))
|
||||
mPOI.thumbnailPath = mString;
|
||||
} else if (localName.equals("wikipediaUrl")) {
|
||||
if (mString != null && !mString.equals(""))
|
||||
mPOI.url = "http://" + mString;
|
||||
} else if (localName.equals("rank")) {
|
||||
mPOI.rank = Integer.parseInt(mString);
|
||||
} else if (localName.equals("entry")) {
|
||||
mPOI.location = new GeoPoint(mLat, mLng);
|
||||
mPOIs.add(mPOI);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
215
src/org/osmdroid/location/GeocoderNominatim.java
Normal file
@ -0,0 +1,215 @@
|
||||
package org.osmdroid.location;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URLEncoder;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
import org.osmdroid.utils.BonusPackHelper;
|
||||
|
||||
import android.content.Context;
|
||||
import android.location.Address;
|
||||
import android.util.Log;
|
||||
|
||||
/**
|
||||
* Implements an equivalent to Android Geocoder class, based on OpenStreetMap
|
||||
* data and Nominatim API. <br>
|
||||
* See http://wiki.openstreetmap.org/wiki/Nominatim or
|
||||
* http://open.mapquestapi.com/nominatim/
|
||||
*
|
||||
* @author M.Kergall
|
||||
*/
|
||||
public class GeocoderNominatim {
|
||||
public static final String NOMINATIM_SERVICE_URL = "http://nominatim.openstreetmap.org/";
|
||||
public static final String MAPQUEST_SERVICE_URL = "http://open.mapquestapi.com/nominatim/v1/";
|
||||
|
||||
protected Locale mLocale;
|
||||
protected String mServiceUrl;
|
||||
|
||||
/**
|
||||
* @param context
|
||||
* ...
|
||||
* @param locale
|
||||
* ...
|
||||
*/
|
||||
protected void init(Context context, Locale locale) {
|
||||
mLocale = locale;
|
||||
setService(NOMINATIM_SERVICE_URL); //default service
|
||||
}
|
||||
|
||||
public GeocoderNominatim(Context context, Locale locale) {
|
||||
init(context, locale);
|
||||
}
|
||||
|
||||
public GeocoderNominatim(Context context) {
|
||||
init(context, Locale.getDefault());
|
||||
}
|
||||
|
||||
static public boolean isPresent() {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify the url of the Nominatim service provider to use. Can be one of
|
||||
* the predefined (NOMINATIM_SERVICE_URL or MAPQUEST_SERVICE_URL), or
|
||||
* another one, your local instance of Nominatim for instance.
|
||||
*
|
||||
* @param serviceUrl
|
||||
* ...
|
||||
*/
|
||||
public void setService(String serviceUrl) {
|
||||
mServiceUrl = serviceUrl;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build an Android Address object from the Nominatim address in JSON
|
||||
* format. Current implementation is mainly targeting french addresses, and
|
||||
* will be quite basic on other countries.
|
||||
*
|
||||
* @param jResult
|
||||
* ...
|
||||
* @return ...
|
||||
* @throws JSONException
|
||||
* ...
|
||||
*/
|
||||
protected Address buildAndroidAddress(JSONObject jResult) throws JSONException {
|
||||
Address gAddress = new Address(mLocale);
|
||||
gAddress.setLatitude(jResult.getDouble("lat"));
|
||||
gAddress.setLongitude(jResult.getDouble("lon"));
|
||||
|
||||
JSONObject jAddress = jResult.getJSONObject("address");
|
||||
|
||||
int addressIndex = 0;
|
||||
if (jAddress.has("road")) {
|
||||
gAddress.setAddressLine(addressIndex++, jAddress.getString("road"));
|
||||
gAddress.setThoroughfare(jAddress.getString("road"));
|
||||
}
|
||||
if (jAddress.has("suburb")) {
|
||||
//gAddress.setAddressLine(addressIndex++, jAddress.getString("suburb"));
|
||||
//not kept => often introduce "noise" in the address.
|
||||
gAddress.setSubLocality(jAddress.getString("suburb"));
|
||||
}
|
||||
if (jAddress.has("postcode")) {
|
||||
gAddress.setAddressLine(addressIndex++, jAddress.getString("postcode"));
|
||||
gAddress.setPostalCode(jAddress.getString("postcode"));
|
||||
}
|
||||
|
||||
if (jAddress.has("city")) {
|
||||
gAddress.setAddressLine(addressIndex++, jAddress.getString("city"));
|
||||
gAddress.setLocality(jAddress.getString("city"));
|
||||
} else if (jAddress.has("town")) {
|
||||
gAddress.setAddressLine(addressIndex++, jAddress.getString("town"));
|
||||
gAddress.setLocality(jAddress.getString("town"));
|
||||
} else if (jAddress.has("village")) {
|
||||
gAddress.setAddressLine(addressIndex++, jAddress.getString("village"));
|
||||
gAddress.setLocality(jAddress.getString("village"));
|
||||
}
|
||||
|
||||
if (jAddress.has("county")) { //France: departement
|
||||
gAddress.setSubAdminArea(jAddress.getString("county"));
|
||||
}
|
||||
if (jAddress.has("state")) { //France: region
|
||||
gAddress.setAdminArea(jAddress.getString("state"));
|
||||
}
|
||||
if (jAddress.has("country")) {
|
||||
gAddress.setAddressLine(addressIndex++, jAddress.getString("country"));
|
||||
gAddress.setCountryName(jAddress.getString("country"));
|
||||
}
|
||||
if (jAddress.has("country_code"))
|
||||
gAddress.setCountryCode(jAddress.getString("country_code"));
|
||||
|
||||
/*
|
||||
* Other possible OSM tags in Nominatim results not handled yet: subway,
|
||||
* golf_course, bus_stop, parking,... house, house_number, building
|
||||
* city_district (13e Arrondissement) road => or highway, ... sub-city
|
||||
* (like suburb) => locality, isolated_dwelling, hamlet ...
|
||||
* state_district
|
||||
*/
|
||||
|
||||
return gAddress;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param latitude
|
||||
* ...
|
||||
* @param longitude
|
||||
* ...
|
||||
* @param maxResults
|
||||
* ...
|
||||
* @return ...
|
||||
* @throws IOException
|
||||
* ...
|
||||
*/
|
||||
public List<Address> getFromLocation(double latitude, double longitude, int maxResults)
|
||||
throws IOException {
|
||||
String url = mServiceUrl
|
||||
+ "reverse?"
|
||||
+ "format=json"
|
||||
+ "&accept-language=" + mLocale.getLanguage()
|
||||
//+ "&addressdetails=1"
|
||||
+ "&lat=" + latitude
|
||||
+ "&lon=" + longitude;
|
||||
Log.d(BonusPackHelper.LOG_TAG, "GeocoderNominatim::getFromLocation:" + url);
|
||||
String result = BonusPackHelper.requestStringFromUrl(url);
|
||||
//Log.d("NOMINATIM", result);
|
||||
if (result == null)
|
||||
throw new IOException();
|
||||
try {
|
||||
JSONObject jResult = new JSONObject(result);
|
||||
Address gAddress = buildAndroidAddress(jResult);
|
||||
List<Address> list = new ArrayList<Address>();
|
||||
list.add(gAddress);
|
||||
return list;
|
||||
} catch (JSONException e) {
|
||||
throw new IOException();
|
||||
}
|
||||
}
|
||||
|
||||
public List<Address> getFromLocationName(String locationName, int maxResults,
|
||||
double lowerLeftLatitude, double lowerLeftLongitude,
|
||||
double upperRightLatitude, double upperRightLongitude)
|
||||
throws IOException {
|
||||
String url = mServiceUrl
|
||||
+ "search?"
|
||||
+ "format=json"
|
||||
+ "&accept-language=" + mLocale.getLanguage()
|
||||
+ "&addressdetails=1"
|
||||
+ "&limit=" + maxResults
|
||||
+ "&q=" + URLEncoder.encode(locationName);
|
||||
if (lowerLeftLatitude != 0.0 && lowerLeftLongitude != 0.0) {
|
||||
//viewbox = left, top, right, bottom:
|
||||
url += "&viewbox=" + lowerLeftLongitude
|
||||
+ "," + upperRightLatitude
|
||||
+ "," + upperRightLongitude
|
||||
+ "," + lowerLeftLatitude
|
||||
+ "&bounded=1";
|
||||
}
|
||||
Log.d(BonusPackHelper.LOG_TAG, "GeocoderNominatim::getFromLocationName:" + url);
|
||||
String result = BonusPackHelper.requestStringFromUrl(url);
|
||||
//Log.d(BonusPackHelper.LOG_TAG, result);
|
||||
if (result == null)
|
||||
throw new IOException();
|
||||
try {
|
||||
JSONArray jResults = new JSONArray(result);
|
||||
List<Address> list = new ArrayList<Address>();
|
||||
for (int i = 0; i < jResults.length(); i++) {
|
||||
JSONObject jResult = jResults.getJSONObject(i);
|
||||
Address gAddress = buildAndroidAddress(jResult);
|
||||
list.add(gAddress);
|
||||
}
|
||||
return list;
|
||||
} catch (JSONException e) {
|
||||
throw new IOException();
|
||||
}
|
||||
}
|
||||
|
||||
public List<Address> getFromLocationName(String locationName, int maxResults)
|
||||
throws IOException {
|
||||
return getFromLocationName(locationName, maxResults, 0.0, 0.0, 0.0, 0.0);
|
||||
}
|
||||
|
||||
}
|
||||
184
src/org/osmdroid/location/NominatimPOIProvider.java
Normal file
@ -0,0 +1,184 @@
|
||||
package org.osmdroid.location;
|
||||
|
||||
import java.net.URLEncoder;
|
||||
import java.util.ArrayList;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
import org.oscim.core.BoundingBox;
|
||||
import org.oscim.core.GeoPoint;
|
||||
import org.osmdroid.utils.BonusPackHelper;
|
||||
|
||||
import android.graphics.Bitmap;
|
||||
import android.util.Log;
|
||||
|
||||
/**
|
||||
* POI Provider using Nominatim service. <br>
|
||||
* See https://wiki.openstreetmap.org/wiki/Nominatim<br>
|
||||
* and http://open.mapquestapi.com/nominatim/<br>
|
||||
*
|
||||
* @author M.Kergall
|
||||
*/
|
||||
public class NominatimPOIProvider {
|
||||
/*
|
||||
* As the doc lacks a lot of features, source code may help:
|
||||
* https://trac.openstreetmap
|
||||
* .org/browser/applications/utils/nominatim/website/search.php featuretype=
|
||||
* to select on feature type (country, city, state, settlement)<br>
|
||||
* format=jsonv2 to get a place_rank<br> offset= to offset the result ?...
|
||||
* <br> polygon=1 to get the border of the poi as a polygon<br> nearlat &
|
||||
* nearlon = ???<br> routewidth/69 and routewidth/30 ???<br>
|
||||
*/
|
||||
public static final String MAPQUEST_POI_SERVICE = "http://open.mapquestapi.com/nominatim/v1/";
|
||||
public static final String NOMINATIM_POI_SERVICE = "http://nominatim.openstreetmap.org/";
|
||||
protected String mService;
|
||||
|
||||
public NominatimPOIProvider() {
|
||||
mService = NOMINATIM_POI_SERVICE;
|
||||
}
|
||||
|
||||
public void setService(String serviceUrl) {
|
||||
mService = serviceUrl;
|
||||
}
|
||||
|
||||
private StringBuffer getCommonUrl(String type, int maxResults) {
|
||||
StringBuffer urlString = new StringBuffer(mService);
|
||||
urlString.append("search?");
|
||||
urlString.append("format=json");
|
||||
urlString.append("&q=[" + URLEncoder.encode(type) + "]");
|
||||
urlString.append("&limit=" + maxResults);
|
||||
urlString.append("&bounded=1");
|
||||
// urlString.append("&addressdetails=0");
|
||||
return urlString;
|
||||
}
|
||||
|
||||
private String getUrlInside(BoundingBox bb, String type, int maxResults) {
|
||||
StringBuffer urlString = getCommonUrl(type, maxResults);
|
||||
urlString.append("&viewbox=" + bb.getMaxLongitude() + ","
|
||||
+ bb.getMaxLatitude() + ","
|
||||
+ bb.getMinLongitude() + ","
|
||||
+ bb.getMinLatitude());
|
||||
return urlString.toString();
|
||||
}
|
||||
|
||||
private String getUrlCloseTo(GeoPoint p, String type,
|
||||
int maxResults, double maxDistance) {
|
||||
int maxD = (int) (maxDistance * 1E6);
|
||||
BoundingBox bb = new BoundingBox(p.latitudeE6 + maxD,
|
||||
p.longitudeE6 + maxD,
|
||||
p.latitudeE6 - maxD,
|
||||
p.longitudeE6 - maxD);
|
||||
return getUrlInside(bb, type, maxResults);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param url
|
||||
* full URL request
|
||||
* @return the list of POI, of null if technical issue.
|
||||
*/
|
||||
public ArrayList<POI> getThem(String url) {
|
||||
Log.d(BonusPackHelper.LOG_TAG, "NominatimPOIProvider:get:" + url);
|
||||
String jString = BonusPackHelper.requestStringFromUrl(url);
|
||||
if (jString == null) {
|
||||
Log.e(BonusPackHelper.LOG_TAG, "NominatimPOIProvider: request failed.");
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
JSONArray jPlaceIds = new JSONArray(jString);
|
||||
int n = jPlaceIds.length();
|
||||
ArrayList<POI> pois = new ArrayList<POI>(n);
|
||||
Bitmap thumbnail = null;
|
||||
for (int i = 0; i < n; i++) {
|
||||
JSONObject jPlace = jPlaceIds.getJSONObject(i);
|
||||
POI poi = new POI(POI.POI_SERVICE_NOMINATIM);
|
||||
poi.id = jPlace.optLong("osm_id");
|
||||
poi.location = new GeoPoint(jPlace.getDouble("lat"), jPlace.getDouble("lon"));
|
||||
poi.category = jPlace.optString("class");
|
||||
poi.type = jPlace.getString("type");
|
||||
poi.description = jPlace.optString("display_name");
|
||||
poi.thumbnailPath = jPlace.optString("icon", null);
|
||||
if (i == 0 && poi.thumbnailPath != null) {
|
||||
//first POI, and we have a thumbnail: load it
|
||||
thumbnail = BonusPackHelper.loadBitmap(poi.thumbnailPath);
|
||||
}
|
||||
poi.thumbnail = thumbnail;
|
||||
pois.add(poi);
|
||||
}
|
||||
return pois;
|
||||
} catch (JSONException e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param position
|
||||
* ...
|
||||
* @param type
|
||||
* an OpenStreetMap feature. See
|
||||
* http://wiki.openstreetmap.org/wiki/Map_Features or
|
||||
* http://code.google.com/p/osmbonuspack/source/browse/trunk/
|
||||
* OSMBonusPackDemo/res/values/poi_tags.xml
|
||||
* @param maxResults
|
||||
* the maximum number of POI returned. Note that in any case,
|
||||
* Nominatim will have an absolute maximum of 100.
|
||||
* @param maxDistance
|
||||
* to the position, in degrees. Note that it is used to build a
|
||||
* bounding box around the position, not a circle.
|
||||
* @return the list of POI, null if technical issue.
|
||||
*/
|
||||
public ArrayList<POI> getPOICloseTo(GeoPoint position, String type,
|
||||
int maxResults, double maxDistance) {
|
||||
String url = getUrlCloseTo(position, type, maxResults, maxDistance);
|
||||
return getThem(url);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param boundingBox
|
||||
* ...
|
||||
* @param type
|
||||
* OpenStreetMap feature
|
||||
* @param maxResults
|
||||
* ...
|
||||
* @return list of POIs, null if technical issue.
|
||||
*/
|
||||
public ArrayList<POI> getPOIInside(BoundingBox boundingBox, String type, int maxResults) {
|
||||
String url = getUrlInside(boundingBox, type, maxResults);
|
||||
return getThem(url);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param path
|
||||
* Warning: a long path may cause a failure due to the url to be
|
||||
* too long. Using a simplified route may help (see
|
||||
* Road.getRouteLow()).
|
||||
* @param type
|
||||
* OpenStreetMap feature
|
||||
* @param maxResults
|
||||
* ...
|
||||
* @param maxWidth
|
||||
* to the path. Certainly not in degrees. Probably in km.
|
||||
* @return list of POIs, null if technical issue.
|
||||
*/
|
||||
public ArrayList<POI> getPOIAlong(ArrayList<GeoPoint> path, String type,
|
||||
int maxResults, double maxWidth) {
|
||||
StringBuffer urlString = getCommonUrl(type, maxResults);
|
||||
urlString.append("&routewidth=" + maxWidth);
|
||||
urlString.append("&route=");
|
||||
boolean isFirst = true;
|
||||
for (GeoPoint p : path) {
|
||||
if (isFirst)
|
||||
isFirst = false;
|
||||
else
|
||||
urlString.append(",");
|
||||
String lat = Double.toString(p.getLatitude());
|
||||
lat = lat.substring(0, Math.min(lat.length(), 7));
|
||||
String lon = Double.toString(p.getLongitude());
|
||||
lon = lon.substring(0, Math.min(lon.length(), 7));
|
||||
urlString.append(lat + "," + lon);
|
||||
//limit the size of url as much as possible, as post method is not supported.
|
||||
}
|
||||
return getThem(urlString.toString());
|
||||
}
|
||||
}
|
||||
177
src/org/osmdroid/location/POI.java
Normal file
@ -0,0 +1,177 @@
|
||||
package org.osmdroid.location;
|
||||
|
||||
import org.oscim.app.R;
|
||||
import org.oscim.core.GeoPoint;
|
||||
import org.osmdroid.utils.BonusPackHelper;
|
||||
|
||||
import android.graphics.Bitmap;
|
||||
import android.os.AsyncTask;
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
import android.widget.ImageView;
|
||||
|
||||
/**
|
||||
* Point of Interest. Exact content may depend of the POI provider used.
|
||||
* @see NominatimPOIProvider
|
||||
* @see GeoNamesPOIProvider
|
||||
* @author M.Kergall
|
||||
*/
|
||||
public class POI implements Parcelable {
|
||||
/** IDs of POI services */
|
||||
public static int POI_SERVICE_NOMINATIM = 100;
|
||||
public static int POI_SERVICE_GEONAMES_WIKIPEDIA = 200;
|
||||
public static int POI_SERVICE_FLICKR = 300;
|
||||
public static int POI_SERVICE_PICASA = 400;
|
||||
|
||||
/** Identifies the service provider of this POI. */
|
||||
public int serviceId;
|
||||
/** Nominatim: OSM ID. GeoNames: 0 */
|
||||
public long id;
|
||||
/** location of the POI */
|
||||
public GeoPoint location;
|
||||
/** Nominatim "class", GeoNames "feature" */
|
||||
public String category;
|
||||
/** type or title */
|
||||
public String type;
|
||||
/** can be the name, the address, a short description */
|
||||
public String description;
|
||||
/** url of the thumbnail. Null if none */
|
||||
public String thumbnailPath;
|
||||
/** the thumbnail itself. Null if none */
|
||||
public Bitmap thumbnail;
|
||||
/** url to a more detailed information page about this POI. Null if none */
|
||||
public String url;
|
||||
/**
|
||||
* popularity of this POI, from 1 (lowest) to 100 (highest). 0 if not
|
||||
* defined.
|
||||
*/
|
||||
public int rank;
|
||||
/** number of attempts to load the thumbnail that have failed */
|
||||
protected int mThumbnailLoadingFailures;
|
||||
|
||||
public POI(int serviceId) {
|
||||
this.serviceId = serviceId;
|
||||
// lets all other fields empty or null. That's fine.
|
||||
}
|
||||
|
||||
protected static int MAX_LOADING_ATTEMPTS = 2;
|
||||
|
||||
/**
|
||||
* @return the POI thumbnail as a Bitmap, if any. If not done yet, it will
|
||||
* load the POI thumbnail from its url (in thumbnailPath field).
|
||||
*/
|
||||
public Bitmap getThumbnail() {
|
||||
if (thumbnail == null && thumbnailPath != null) {
|
||||
Log.d(BonusPackHelper.LOG_TAG, "POI:load thumbnail:" + thumbnailPath);
|
||||
thumbnail = BonusPackHelper.loadBitmap(thumbnailPath);
|
||||
if (thumbnail == null) {
|
||||
mThumbnailLoadingFailures++;
|
||||
if (mThumbnailLoadingFailures >= MAX_LOADING_ATTEMPTS) {
|
||||
// this path really doesn't work, "kill" it for next calls:
|
||||
thumbnailPath = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
return thumbnail;
|
||||
}
|
||||
|
||||
// http://stackoverflow.com/questions/7729133/using-asynctask-to-load-images-in-listview
|
||||
// TODO see link, there might be a better solution
|
||||
/**
|
||||
* Fetch the thumbnail from its url on a thread.
|
||||
* @param imageView
|
||||
* to update once the thumbnail is retrieved, or to hide if no
|
||||
* thumbnail.
|
||||
*/
|
||||
public void fetchThumbnail(final ImageView imageView) {
|
||||
if (thumbnail != null) {
|
||||
imageView.setImageBitmap(thumbnail);
|
||||
imageView.setVisibility(View.VISIBLE);
|
||||
} else if (thumbnailPath != null) {
|
||||
imageView.setImageResource(R.drawable.ic_empty);
|
||||
new ThumbnailTask(imageView).execute(imageView);
|
||||
} else {
|
||||
imageView.setVisibility(View.GONE);
|
||||
}
|
||||
}
|
||||
|
||||
class ThumbnailTask extends AsyncTask<ImageView, Void, ImageView> {
|
||||
|
||||
public ThumbnailTask(ImageView iv) {
|
||||
iv.setTag(thumbnailPath);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ImageView doInBackground(ImageView... params) {
|
||||
getThumbnail();
|
||||
return params[0];
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(ImageView iv) {
|
||||
if (thumbnailPath.equals(iv.getTag().toString()))
|
||||
iv.setImageBitmap(thumbnail);
|
||||
}
|
||||
}
|
||||
|
||||
// --- Parcelable implementation
|
||||
|
||||
@Override
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeToParcel(Parcel out, int flags) {
|
||||
out.writeInt(serviceId);
|
||||
out.writeLong(id);
|
||||
out.writeParcelable(location, 0);
|
||||
out.writeString(category);
|
||||
out.writeString(type);
|
||||
out.writeString(description);
|
||||
out.writeString(thumbnailPath);
|
||||
out.writeParcelable(thumbnail, 0);
|
||||
out.writeString(url);
|
||||
out.writeInt(rank);
|
||||
out.writeInt(mThumbnailLoadingFailures);
|
||||
}
|
||||
|
||||
public static final Parcelable.Creator<POI> CREATOR = new Parcelable.Creator<POI>() {
|
||||
@Override
|
||||
public POI createFromParcel(Parcel in) {
|
||||
POI poi = new POI(in.readInt());
|
||||
poi.id = in.readLong();
|
||||
poi.location = in.readParcelable(GeoPoint.class.getClassLoader());
|
||||
poi.category = in.readString();
|
||||
poi.type = in.readString();
|
||||
poi.description = in.readString();
|
||||
poi.thumbnailPath = in.readString();
|
||||
poi.thumbnail = in.readParcelable(Bitmap.class.getClassLoader());
|
||||
poi.url = in.readString();
|
||||
poi.rank = in.readInt();
|
||||
poi.mThumbnailLoadingFailures = in.readInt();
|
||||
return poi;
|
||||
}
|
||||
|
||||
@Override
|
||||
public POI[] newArray(int size) {
|
||||
return new POI[size];
|
||||
}
|
||||
};
|
||||
|
||||
// private POI(Parcel in) {
|
||||
// serviceId = in.readInt();
|
||||
// id = in.readLong();
|
||||
// location = in.readParcelable(GeoPoint.class.getClassLoader());
|
||||
// category = in.readString();
|
||||
// type = in.readString();
|
||||
// description = in.readString();
|
||||
// thumbnailPath = in.readString();
|
||||
// thumbnail = in.readParcelable(Bitmap.class.getClassLoader());
|
||||
// url = in.readString();
|
||||
// rank = in.readInt();
|
||||
// mThumbnailLoadingFailures = in.readInt();
|
||||
// }
|
||||
}
|
||||
155
src/org/osmdroid/location/PicasaPOIProvider.java
Normal file
@ -0,0 +1,155 @@
|
||||
package org.osmdroid.location;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.URLEncoder;
|
||||
import java.util.ArrayList;
|
||||
|
||||
import javax.xml.parsers.ParserConfigurationException;
|
||||
import javax.xml.parsers.SAXParser;
|
||||
import javax.xml.parsers.SAXParserFactory;
|
||||
|
||||
import org.oscim.core.BoundingBox;
|
||||
import org.oscim.core.GeoPoint;
|
||||
import org.osmdroid.utils.BonusPackHelper;
|
||||
import org.osmdroid.utils.HttpConnection;
|
||||
import org.xml.sax.Attributes;
|
||||
import org.xml.sax.SAXException;
|
||||
import org.xml.sax.helpers.DefaultHandler;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
/** POI Provider using Picasa service.
|
||||
* @see "https://developers.google.com/picasa-web/docs/2.0/reference"
|
||||
* @author M.Kergall */
|
||||
public class PicasaPOIProvider {
|
||||
|
||||
String mAccessToken;
|
||||
|
||||
/** @param accessToken
|
||||
* the account to give to the service. Null for public access.
|
||||
* @see "https://developers.google.com/picasa-web/docs/2.0/developers_guide_protocol#CreatingAccount" */
|
||||
public PicasaPOIProvider(String accessToken) {
|
||||
mAccessToken = accessToken;
|
||||
}
|
||||
|
||||
private String getUrlInside(BoundingBox boundingBox, int maxResults, String query) {
|
||||
StringBuffer url = new StringBuffer("http://picasaweb.google.com/data/feed/api/all?");
|
||||
url.append("bbox=" + boundingBox.getMinLongitude());
|
||||
url.append("," + boundingBox.getMinLatitude());
|
||||
url.append("," + boundingBox.getMaxLongitude());
|
||||
url.append("," + boundingBox.getMaxLatitude());
|
||||
url.append("&max-results=" + maxResults);
|
||||
url.append("&thumbsize=64c"); //thumbnail size: 64, cropped.
|
||||
url.append("&fields=openSearch:totalResults,entry(summary,media:group/media:thumbnail,media:group/media:title,gphoto:*,georss:where,link)");
|
||||
if (query != null)
|
||||
url.append("&q=" + URLEncoder.encode(query));
|
||||
if (mAccessToken != null) {
|
||||
//TODO: warning: not tested...
|
||||
url.append("&access_token=" + mAccessToken);
|
||||
}
|
||||
return url.toString();
|
||||
}
|
||||
|
||||
public ArrayList<POI> getThem(String fullUrl) {
|
||||
Log.d(BonusPackHelper.LOG_TAG, "PicasaPOIProvider:get:" + fullUrl);
|
||||
HttpConnection connection = new HttpConnection();
|
||||
connection.doGet(fullUrl);
|
||||
InputStream stream = connection.getStream();
|
||||
if (stream == null) {
|
||||
return null;
|
||||
}
|
||||
PicasaXMLHandler handler = new PicasaXMLHandler();
|
||||
try {
|
||||
SAXParser parser = SAXParserFactory.newInstance().newSAXParser();
|
||||
parser.getXMLReader().setFeature("http://xml.org/sax/features/namespaces", false);
|
||||
parser.getXMLReader()
|
||||
.setFeature("http://xml.org/sax/features/namespace-prefixes", true);
|
||||
parser.parse(stream, handler);
|
||||
} catch (ParserConfigurationException e) {
|
||||
e.printStackTrace();
|
||||
} catch (SAXException e) {
|
||||
e.printStackTrace();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
connection.close();
|
||||
if (handler.mPOIs != null)
|
||||
Log.d(BonusPackHelper.LOG_TAG, "done:" + handler.mPOIs.size() + " got, on a total of:"
|
||||
+ handler.mTotalResults);
|
||||
return handler.mPOIs;
|
||||
}
|
||||
|
||||
/** @param boundingBox
|
||||
* ...
|
||||
* @param maxResults
|
||||
* ...
|
||||
* @param query
|
||||
* - optional - full-text query string. Searches the title,
|
||||
* caption and tags for the specified string value.
|
||||
* @return list of POI, Picasa photos inside the bounding box. Null if
|
||||
* technical issue. */
|
||||
public ArrayList<POI> getPOIInside(BoundingBox boundingBox, int maxResults, String query) {
|
||||
String url = getUrlInside(boundingBox, maxResults, query);
|
||||
return getThem(url);
|
||||
}
|
||||
}
|
||||
|
||||
class PicasaXMLHandler extends DefaultHandler {
|
||||
|
||||
private String mString;
|
||||
double mLat, mLng;
|
||||
POI mPOI;
|
||||
ArrayList<POI> mPOIs;
|
||||
int mTotalResults;
|
||||
|
||||
public PicasaXMLHandler() {
|
||||
mPOIs = new ArrayList<POI>();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startElement(String uri, String localName, String qName,
|
||||
Attributes attributes) {
|
||||
if (qName.equals("entry")) {
|
||||
mPOI = new POI(POI.POI_SERVICE_PICASA);
|
||||
} else if (qName.equals("media:thumbnail")) {
|
||||
mPOI.thumbnailPath = attributes.getValue("url");
|
||||
} else if (qName.equals("link")) {
|
||||
String rel = attributes.getValue("rel");
|
||||
if ("http://schemas.google.com/photos/2007#canonical".equals(rel)) {
|
||||
mPOI.url = attributes.getValue("href");
|
||||
}
|
||||
}
|
||||
mString = new String();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void characters(char[] ch, int start, int length) {
|
||||
String chars = new String(ch, start, length);
|
||||
mString = mString.concat(chars);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void endElement(String uri, String localName, String qName) {
|
||||
if (qName.equals("gml:pos")) {
|
||||
String[] coords = mString.split(" ");
|
||||
mLat = Double.parseDouble(coords[0]);
|
||||
mLng = Double.parseDouble(coords[1]);
|
||||
} else if (qName.equals("gphoto:id")) {
|
||||
mPOI.id = Long.parseLong(mString);
|
||||
} else if (qName.equals("media:title")) {
|
||||
mPOI.type = mString;
|
||||
} else if (qName.equals("summary")) {
|
||||
mPOI.description = mString;
|
||||
} else if (qName.equals("gphoto:albumtitle")) {
|
||||
mPOI.category = mString;
|
||||
} else if (qName.equals("entry")) {
|
||||
mPOI.location = new GeoPoint(mLat, mLng);
|
||||
mPOIs.add(mPOI);
|
||||
mPOI = null;
|
||||
} else if (qName.equals("openSearch:totalResults")) {
|
||||
mTotalResults = Integer.parseInt(mString);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
100
src/org/osmdroid/overlays/DefaultInfoWindow.java
Normal file
@ -0,0 +1,100 @@
|
||||
package org.osmdroid.overlays;
|
||||
|
||||
import org.oscim.view.MapView;
|
||||
import org.osmdroid.utils.BonusPackHelper;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
|
||||
/**
|
||||
* Default implementation of InfoWindow. It handles a text and a description. It
|
||||
* also handles optionally a sub-description and an image. Clicking on the
|
||||
* bubble will close it.
|
||||
*
|
||||
* @author M.Kergall
|
||||
*/
|
||||
public class DefaultInfoWindow extends InfoWindow {
|
||||
|
||||
// resource ids
|
||||
private static int mTitleId = 0, mDescriptionId = 0, mSubDescriptionId = 0, mImageId = 0;
|
||||
|
||||
private static void setResIds(Context context) {
|
||||
// get application package name
|
||||
String packageName = context.getPackageName();
|
||||
mTitleId = context.getResources().getIdentifier("id/bubble_title", null, packageName);
|
||||
|
||||
mDescriptionId = context.getResources().getIdentifier("id/bubble_description", null,
|
||||
packageName);
|
||||
|
||||
mSubDescriptionId = context.getResources().getIdentifier("id/bubble_subdescription", null,
|
||||
packageName);
|
||||
|
||||
mImageId = context.getResources().getIdentifier("id/bubble_image", null, packageName);
|
||||
|
||||
if (mTitleId == 0 || mDescriptionId == 0) {
|
||||
Log.e(BonusPackHelper.LOG_TAG, "DefaultInfoWindow: unable to get res ids in "
|
||||
+ packageName);
|
||||
}
|
||||
}
|
||||
|
||||
public DefaultInfoWindow(int layoutResId, MapView mapView) {
|
||||
super(layoutResId, mapView);
|
||||
|
||||
if (mTitleId == 0)
|
||||
setResIds(mapView.getContext());
|
||||
|
||||
// default behaviour: close it when clicking on the bubble:
|
||||
mView.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
close();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onOpen(ExtendedOverlayItem item) {
|
||||
String title = item.getTitle();
|
||||
if (title == null)
|
||||
title = "";
|
||||
|
||||
((TextView) mView.findViewById(mTitleId)).setText(title);
|
||||
|
||||
String snippet = item.getDescription();
|
||||
if (snippet == null)
|
||||
snippet = "";
|
||||
|
||||
((TextView) mView.findViewById(mDescriptionId)).setText(snippet);
|
||||
|
||||
// handle sub-description, hidding or showing the text view:
|
||||
TextView subDescText = (TextView) mView.findViewById(mSubDescriptionId);
|
||||
String subDesc = item.getSubDescription();
|
||||
if (subDesc != null && !("".equals(subDesc))) {
|
||||
subDescText.setText(subDesc);
|
||||
subDescText.setVisibility(View.VISIBLE);
|
||||
} else {
|
||||
subDescText.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
// handle image
|
||||
ImageView imageView = (ImageView) mView.findViewById(mImageId);
|
||||
Drawable image = item.getImage();
|
||||
if (image != null) {
|
||||
// or setBackgroundDrawable(image)?
|
||||
imageView.setImageDrawable(image);
|
||||
imageView.setVisibility(View.VISIBLE);
|
||||
} else
|
||||
imageView.setVisibility(View.GONE);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClose() {
|
||||
// by default, do nothing
|
||||
}
|
||||
|
||||
}
|
||||
168
src/org/osmdroid/overlays/ExtendedOverlayItem.java
Normal file
@ -0,0 +1,168 @@
|
||||
package org.osmdroid.overlays;
|
||||
|
||||
import org.oscim.core.GeoPoint;
|
||||
import org.oscim.overlay.OverlayItem;
|
||||
import org.oscim.view.MapView;
|
||||
|
||||
import android.graphics.Point;
|
||||
import android.graphics.drawable.Drawable;
|
||||
|
||||
/**
|
||||
* An OverlayItem to use in ItemizedOverlayWithBubble<br>
|
||||
* - more complete: can contain an image and a sub-description that will be
|
||||
* displayed in the bubble, <br>
|
||||
* - and flexible: attributes are modifiable<br>
|
||||
* Known Issues:<br>
|
||||
* - Bubble offset is not perfect on h&xhdpi resolutions, due to an osmdroid
|
||||
* issue on marker drawing<br>
|
||||
* - Bubble offset is at 0 when using the default marker => set the marker on
|
||||
* each item!<br>
|
||||
* @see ItemizedOverlayWithBubble
|
||||
* @author M.Kergall
|
||||
*/
|
||||
public class ExtendedOverlayItem extends OverlayItem {
|
||||
|
||||
// now, they are modifiable
|
||||
private String mTitle, mDescription;
|
||||
// now, they are modifiable
|
||||
// a third field that can be displayed in
|
||||
// the infowindow, on a third line
|
||||
// that will be shown in the infowindow.
|
||||
//unfortunately, this is not so simple...
|
||||
private String mSubDescription;
|
||||
private Drawable mImage;
|
||||
private Object mRelatedObject; // reference to an object (of any kind)
|
||||
// linked to this item.
|
||||
|
||||
public ExtendedOverlayItem(String aTitle, String aDescription, GeoPoint aGeoPoint) {
|
||||
super(aTitle, aDescription, aGeoPoint);
|
||||
mTitle = aTitle;
|
||||
mDescription = aDescription;
|
||||
mSubDescription = null;
|
||||
mImage = null;
|
||||
mRelatedObject = null;
|
||||
}
|
||||
|
||||
public void setTitle(String aTitle) {
|
||||
mTitle = aTitle;
|
||||
}
|
||||
|
||||
public void setDescription(String aDescription) {
|
||||
mDescription = aDescription;
|
||||
}
|
||||
|
||||
public void setSubDescription(String aSubDescription) {
|
||||
mSubDescription = aSubDescription;
|
||||
}
|
||||
|
||||
public void setImage(Drawable anImage) {
|
||||
mImage = anImage;
|
||||
}
|
||||
|
||||
public void setRelatedObject(Object o) {
|
||||
mRelatedObject = o;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getTitle() {
|
||||
return mTitle;
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return mDescription;
|
||||
}
|
||||
|
||||
public String getSubDescription() {
|
||||
return mSubDescription;
|
||||
}
|
||||
|
||||
public Drawable getImage() {
|
||||
return mImage;
|
||||
}
|
||||
|
||||
public Object getRelatedObject() {
|
||||
return mRelatedObject;
|
||||
}
|
||||
|
||||
/**
|
||||
* From a HotspotPlace and drawable dimensions (width, height), return the
|
||||
* hotspot position. Could be a public method of HotspotPlace or
|
||||
* OverlayItem...
|
||||
* @param place
|
||||
* ...
|
||||
* @param w
|
||||
* ...
|
||||
* @param h
|
||||
* ...
|
||||
* @return ...
|
||||
*/
|
||||
public Point getHotspot(HotspotPlace place, int w, int h) {
|
||||
Point hp = new Point();
|
||||
if (place == null)
|
||||
place = HotspotPlace.BOTTOM_CENTER; // use same default than in
|
||||
// osmdroid.
|
||||
switch (place) {
|
||||
case NONE:
|
||||
hp.set(0, 0);
|
||||
break;
|
||||
case BOTTOM_CENTER:
|
||||
hp.set(w / 2, 0);
|
||||
break;
|
||||
case LOWER_LEFT_CORNER:
|
||||
hp.set(0, 0);
|
||||
break;
|
||||
case LOWER_RIGHT_CORNER:
|
||||
hp.set(w, 0);
|
||||
break;
|
||||
case CENTER:
|
||||
hp.set(w / 2, -h / 2);
|
||||
break;
|
||||
case LEFT_CENTER:
|
||||
hp.set(0, -h / 2);
|
||||
break;
|
||||
case RIGHT_CENTER:
|
||||
hp.set(w, -h / 2);
|
||||
break;
|
||||
case TOP_CENTER:
|
||||
hp.set(w / 2, -h);
|
||||
break;
|
||||
case UPPER_LEFT_CORNER:
|
||||
hp.set(0, -h);
|
||||
break;
|
||||
case UPPER_RIGHT_CORNER:
|
||||
hp.set(w, -h);
|
||||
break;
|
||||
}
|
||||
return hp;
|
||||
}
|
||||
|
||||
/**
|
||||
* Populates this bubble with all item info:
|
||||
* <ul>
|
||||
* title and description in any case,
|
||||
* </ul>
|
||||
* <ul>
|
||||
* image and sub-description if any.
|
||||
* </ul>
|
||||
* and centers the map on the item. <br>
|
||||
* @param bubble
|
||||
* ...
|
||||
* @param mapView
|
||||
* ...
|
||||
*/
|
||||
public void showBubble(InfoWindow bubble, MapView mapView) {
|
||||
// offset the bubble to be top-centered on the marker:
|
||||
Drawable marker = getMarker(0 /* OverlayItem.ITEM_STATE_FOCUSED_MASK */);
|
||||
int markerWidth = 0, markerHeight = 0;
|
||||
if (marker != null) {
|
||||
markerWidth = marker.getIntrinsicWidth();
|
||||
markerHeight = marker.getIntrinsicHeight();
|
||||
} // else... we don't have the default marker size => don't user default
|
||||
// markers!!!
|
||||
Point markerH = getHotspot(getMarkerHotspot(), markerWidth, markerHeight);
|
||||
Point bubbleH = getHotspot(HotspotPlace.TOP_CENTER, markerWidth, markerHeight);
|
||||
bubbleH.offset(-markerH.x, -markerH.y);
|
||||
|
||||
bubble.open(this, bubbleH.x, bubbleH.y);
|
||||
}
|
||||
}
|
||||
140
src/org/osmdroid/overlays/InfoWindow.java
Normal file
@ -0,0 +1,140 @@
|
||||
package org.osmdroid.overlays;
|
||||
|
||||
// TODO composite view as texture overlay and only allow one bubble at a time.
|
||||
|
||||
import org.oscim.view.MapView;
|
||||
|
||||
import android.content.Context;
|
||||
import android.view.Gravity;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.RelativeLayout;
|
||||
|
||||
/**
|
||||
* View that can be displayed on an OSMDroid map, associated to a GeoPoint.
|
||||
* Typical usage: cartoon-like bubbles displayed when clicking an overlay item.
|
||||
* It mimics the InfoWindow class of Google Maps JavaScript API V3. Main
|
||||
* differences are:
|
||||
* <ul>
|
||||
* <li>Structure and content of the view is let to the responsibility of the
|
||||
* caller.</li>
|
||||
* <li>The same InfoWindow can be associated to many items.</li>
|
||||
* </ul>
|
||||
* Known issues:
|
||||
* <ul>
|
||||
* <li>It disappears when zooming in/out (osmdroid issue #259 on osmdroid 3.0.8,
|
||||
* should be fixed in next version).</li>
|
||||
* <li>The window is displayed "above" the marker, so the queue of the bubble
|
||||
* can hide the marker.</li>
|
||||
* </ul>
|
||||
* This is an abstract class.
|
||||
* @see DefaultInfoWindow
|
||||
* @author M.Kergall
|
||||
*/
|
||||
public abstract class InfoWindow {
|
||||
|
||||
protected View mView;
|
||||
protected boolean mIsVisible = false;
|
||||
protected MapView mMapView;
|
||||
protected RelativeLayout mLayout;
|
||||
private android.widget.RelativeLayout.LayoutParams mLayoutPos;
|
||||
|
||||
/**
|
||||
* @param layoutResId
|
||||
* the id of the view resource.
|
||||
* @param mapView
|
||||
* the mapview on which is hooked the view
|
||||
*/
|
||||
public InfoWindow(int layoutResId, MapView mapView) {
|
||||
mMapView = mapView;
|
||||
|
||||
mIsVisible = false;
|
||||
ViewGroup parent = (ViewGroup) mapView.getParent();
|
||||
Context context = mapView.getContext();
|
||||
LayoutInflater inflater = (LayoutInflater) context
|
||||
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
|
||||
mView = inflater.inflate(layoutResId, parent, false);
|
||||
|
||||
RelativeLayout.LayoutParams rlp =
|
||||
new RelativeLayout.LayoutParams(
|
||||
android.view.ViewGroup.LayoutParams.MATCH_PARENT,
|
||||
android.view.ViewGroup.LayoutParams.MATCH_PARENT);
|
||||
|
||||
mLayout = new RelativeLayout(context);
|
||||
mLayout.setGravity(Gravity.CENTER_HORIZONTAL | Gravity.BOTTOM);
|
||||
mLayout.setLayoutParams(rlp);
|
||||
mLayoutPos = rlp;
|
||||
|
||||
// not so sure about this. why is just blitting the bitmap on glview so slow?...
|
||||
mView.setDrawingCacheEnabled(true);
|
||||
// mLayout.setDrawingCacheEnabled(true);
|
||||
// mLayout.setPersistentDrawingCache(ViewGroup.PERSISTENT_ALL_CACHES);
|
||||
// mLayout.setAlwaysDrawnWithCacheEnabled(true); // call this method
|
||||
mLayout.setWillNotDraw(true);
|
||||
mLayout.addView(mView);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the Android view. This allows to set its content.
|
||||
* @return the Android view
|
||||
*/
|
||||
public View getView() {
|
||||
return (mView);
|
||||
}
|
||||
|
||||
/**
|
||||
* open the window at the specified position.
|
||||
* @param item
|
||||
* the item on which is hooked the view
|
||||
* @param offsetX
|
||||
* (&offsetY) the offset of the view to the position, in pixels.
|
||||
* This allows to offset the view from the marker position.
|
||||
* @param offsetY
|
||||
* ...
|
||||
*/
|
||||
public void open(ExtendedOverlayItem item, int offsetX, int offsetY) {
|
||||
onOpen(item);
|
||||
|
||||
close(); // if it was already opened
|
||||
// mView.requestLayout();
|
||||
mView.buildDrawingCache();
|
||||
mMapView.addView(mLayout);
|
||||
mIsVisible = true;
|
||||
}
|
||||
|
||||
public void position(int x, int y) {
|
||||
// if this isnt madness...
|
||||
RelativeLayout.LayoutParams rlp = mLayoutPos;
|
||||
rlp.leftMargin = x;
|
||||
rlp.rightMargin = -x;
|
||||
rlp.topMargin = -y;
|
||||
rlp.bottomMargin = y + mMapView.getHeight() / 2;
|
||||
mLayout.setLayoutParams(rlp);
|
||||
|
||||
//mMapView.requestLayout();
|
||||
mLayout.requestLayout();
|
||||
|
||||
// using scrollTo the bubble somehow does not appear when it
|
||||
// is not already in viewport...
|
||||
// mLayout.scrollTo(-x, y + mMapView.getHeight() / 2);
|
||||
}
|
||||
|
||||
public void close() {
|
||||
if (mIsVisible) {
|
||||
mIsVisible = false;
|
||||
((ViewGroup) mLayout.getParent()).removeView(mLayout);
|
||||
onClose();
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isOpen() {
|
||||
return mIsVisible;
|
||||
}
|
||||
|
||||
// Abstract methods to implement:
|
||||
public abstract void onOpen(ExtendedOverlayItem item);
|
||||
|
||||
public abstract void onClose();
|
||||
|
||||
}
|
||||
199
src/org/osmdroid/overlays/ItemizedOverlayWithBubble.java
Normal file
@ -0,0 +1,199 @@
|
||||
package org.osmdroid.overlays;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.oscim.core.GeoPoint;
|
||||
import org.oscim.core.MapPosition;
|
||||
import org.oscim.overlay.ItemizedIconOverlay;
|
||||
import org.oscim.overlay.OverlayItem;
|
||||
import org.oscim.view.MapView;
|
||||
import org.osmdroid.utils.BonusPackHelper;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Point;
|
||||
import android.util.Log;
|
||||
|
||||
/**
|
||||
* An itemized overlay with an InfoWindow or "bubble" which opens when the user
|
||||
* taps on an overlay item, and displays item attributes. <br>
|
||||
* Items must be ExtendedOverlayItem. <br>
|
||||
* @see ExtendedOverlayItem
|
||||
* @see InfoWindow
|
||||
* @author M.Kergall
|
||||
* @param <Item>
|
||||
* ...
|
||||
*/
|
||||
public class ItemizedOverlayWithBubble<Item extends OverlayItem> extends ItemizedIconOverlay<Item>
|
||||
implements ItemizedIconOverlay.OnItemGestureListener<Item>
|
||||
{
|
||||
|
||||
protected List<Item> mItemsList;
|
||||
|
||||
// only one for all items of this overlay => one at a time
|
||||
protected InfoWindow mBubble;
|
||||
|
||||
// the item currently showing the bubble. Null if none.
|
||||
protected OverlayItem mItemWithBubble;
|
||||
|
||||
static int layoutResId = 0;
|
||||
|
||||
@Override
|
||||
public boolean onItemLongPress(final int index, final OverlayItem item) {
|
||||
if (mBubble.isOpen())
|
||||
hideBubble();
|
||||
else
|
||||
showBubble(index);
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onItemSingleTapUp(final int index, final OverlayItem item) {
|
||||
showBubble(index);
|
||||
return false;
|
||||
}
|
||||
|
||||
private Point mTmpPoint = new Point();
|
||||
|
||||
@Override
|
||||
public void onUpdate(MapPosition mapPosition) {
|
||||
if (mBubble.isOpen()) {
|
||||
GeoPoint gp = mItemWithBubble.getPoint();
|
||||
Point p = mTmpPoint;
|
||||
|
||||
mMapView.getMapViewPosition().project(gp, p);
|
||||
|
||||
mBubble.position(p.x, p.y);
|
||||
}
|
||||
}
|
||||
|
||||
public ItemizedOverlayWithBubble(final MapView mapView, final Context context,
|
||||
final List<Item> aList, final InfoWindow bubble) {
|
||||
|
||||
super(mapView, context, aList, null);
|
||||
|
||||
mItemsList = aList;
|
||||
if (bubble != null) {
|
||||
mBubble = bubble;
|
||||
} else {
|
||||
// build default bubble:
|
||||
String packageName = context.getPackageName();
|
||||
if (layoutResId == 0) {
|
||||
layoutResId = context.getResources().getIdentifier(
|
||||
"layout/bonuspack_bubble", null,
|
||||
packageName);
|
||||
if (layoutResId == 0)
|
||||
Log.e(BonusPackHelper.LOG_TAG,
|
||||
"ItemizedOverlayWithBubble: layout/bonuspack_bubble not found in "
|
||||
+ packageName);
|
||||
}
|
||||
mBubble = new DefaultInfoWindow(layoutResId, mapView);
|
||||
}
|
||||
mItemWithBubble = null;
|
||||
|
||||
mOnItemGestureListener = this;
|
||||
}
|
||||
|
||||
public ItemizedOverlayWithBubble(final Context context, final List<Item> aList,
|
||||
final MapView mapView) {
|
||||
this(mapView, context, aList, null);
|
||||
}
|
||||
|
||||
void showBubble(int index) {
|
||||
showBubbleOnItem(index, mMapView);
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens the bubble on the item. For each ItemizedOverlay, only one bubble
|
||||
* is opened at a time. If you want more bubbles opened simultaneously, use
|
||||
* many ItemizedOverlays.
|
||||
* @param index
|
||||
* of the overlay item to show
|
||||
* @param mapView
|
||||
* ...
|
||||
*/
|
||||
public void showBubbleOnItem(final int index, final MapView mapView) {
|
||||
ExtendedOverlayItem eItem = (ExtendedOverlayItem) (getItem(index));
|
||||
mItemWithBubble = eItem;
|
||||
if (eItem != null) {
|
||||
eItem.showBubble(mBubble, mapView);
|
||||
// mMapView.getMapViewPosition().animateTo(eItem.mGeoPoint, 0);
|
||||
|
||||
mapView.redrawMap();
|
||||
setFocus((Item) eItem);
|
||||
}
|
||||
}
|
||||
|
||||
/** Close the bubble (if it's opened). */
|
||||
public void hideBubble() {
|
||||
mBubble.close();
|
||||
mItemWithBubble = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean onSingleTapUpHelper(final int index, final Item item, final MapView mapView) {
|
||||
showBubbleOnItem(index, mapView);
|
||||
return true;
|
||||
}
|
||||
|
||||
/** @return the item currenty showing the bubble, or null if none. */
|
||||
public OverlayItem getBubbledItem() {
|
||||
if (mBubble.isOpen())
|
||||
return mItemWithBubble;
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/** @return the index of the item currenty showing the bubble, or -1 if none. */
|
||||
public int getBubbledItemId() {
|
||||
OverlayItem item = getBubbledItem();
|
||||
if (item == null)
|
||||
return -1;
|
||||
|
||||
return mItemsList.indexOf(item);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean removeItem(final Item item) {
|
||||
boolean result = super.removeItem(item);
|
||||
if (mItemWithBubble == item) {
|
||||
hideBubble();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeAllItems() {
|
||||
super.removeAllItems();
|
||||
hideBubble();
|
||||
}
|
||||
|
||||
// FIXME @Override
|
||||
// public void draw(final Canvas canvas, final MapView mapView, final boolean shadow) {
|
||||
// // 1. Fixing drawing focused item on top in ItemizedOverlay (osmdroid
|
||||
// // issue 354):
|
||||
// if (shadow) {
|
||||
// return;
|
||||
// }
|
||||
// final Projection pj = mapView.getProjection();
|
||||
// final int size = mItemsList.size() - 1;
|
||||
// final Point mCurScreenCoords = new Point();
|
||||
//
|
||||
// /*
|
||||
// * Draw in backward cycle, so the items with the least index are on the
|
||||
// * front.
|
||||
// */
|
||||
// for (int i = size; i >= 0; i--) {
|
||||
// final Item item = getItem(i);
|
||||
// if (item != mItemWithBubble) {
|
||||
// pj.toMapPixels(item.mGeoPoint, mCurScreenCoords);
|
||||
// onDrawItem(canvas, item, mCurScreenCoords);
|
||||
// }
|
||||
// }
|
||||
// // draw focused item last:
|
||||
// if (mItemWithBubble != null) {
|
||||
// pj.toMapPixels(mItemWithBubble.mGeoPoint, mCurScreenCoords);
|
||||
// onDrawItem(canvas, (Item) mItemWithBubble, mCurScreenCoords);
|
||||
// }
|
||||
// }
|
||||
|
||||
}
|
||||
58
src/org/osmdroid/overlays/MapEventsOverlay.java
Normal file
@ -0,0 +1,58 @@
|
||||
package org.osmdroid.overlays;
|
||||
|
||||
import org.oscim.core.GeoPoint;
|
||||
import org.oscim.overlay.Overlay;
|
||||
import org.oscim.view.MapView;
|
||||
|
||||
import android.content.Context;
|
||||
import android.view.MotionEvent;
|
||||
|
||||
/**
|
||||
* Empty overlay than can be used to detect events on the map, and to throw them
|
||||
* to a MapEventsReceiver.
|
||||
*
|
||||
* @see MapEventsReceiver
|
||||
* @author M.Kergall
|
||||
*/
|
||||
public class MapEventsOverlay extends Overlay {
|
||||
|
||||
private MapEventsReceiver mReceiver;
|
||||
|
||||
/**
|
||||
* @param ctx
|
||||
* the context
|
||||
* @param receiver
|
||||
* the object that will receive/handle the events. It must
|
||||
* implement MapEventsReceiver interface.
|
||||
*/
|
||||
public MapEventsOverlay(Context ctx, MapEventsReceiver receiver) {
|
||||
super(ctx);
|
||||
mReceiver = receiver;
|
||||
}
|
||||
|
||||
// @Override
|
||||
// protected void draw(Canvas c, MapView osmv, boolean shadow) {
|
||||
// // Nothing to draw
|
||||
// }
|
||||
|
||||
@Override
|
||||
public boolean onSingleTapUp(MotionEvent e, MapView mapView) {
|
||||
// Projection proj = mapView.getProjection();
|
||||
// GeoPoint p = proj.fromPixels(e.getX(), e.getY());
|
||||
GeoPoint p = mapView.getMapViewPosition().fromScreenPixels(e.getX(), e.getY());
|
||||
|
||||
return mReceiver.singleTapUpHelper(p);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onLongPress(MotionEvent e, MapView mapView) {
|
||||
// Projection proj = mapView.getProjection();
|
||||
// GeoPoint p = proj.fromPixels(e.getX(), e.getY());
|
||||
|
||||
GeoPoint p = mapView.getMapViewPosition().fromScreenPixels(e.getX(), e.getY());
|
||||
|
||||
// throw event to the receiver:
|
||||
return mReceiver.longPressHelper(p);
|
||||
}
|
||||
|
||||
}
|
||||