├── .idea
├── .name
├── copyright
│ └── profiles_settings.xml
├── encodings.xml
├── vcs.xml
├── inspectionProfiles
│ ├── profiles_settings.xml
│ └── Project_Default.xml
├── modules.xml
├── runConfigurations.xml
├── compiler.xml
├── gradle.xml
├── misc.xml
└── codeStyleSettings.xml
├── app
├── .gitignore
├── src
│ ├── main
│ │ ├── res
│ │ │ ├── values
│ │ │ │ ├── strings.xml
│ │ │ │ ├── dimens.xml
│ │ │ │ ├── colors.xml
│ │ │ │ ├── styles.xml
│ │ │ │ └── config.xml
│ │ │ ├── mipmap-xhdpi
│ │ │ │ ├── compass.png
│ │ │ │ └── ic_launcher.png
│ │ │ ├── mipmap-xxhdpi
│ │ │ │ ├── compass.png
│ │ │ │ └── ic_launcher.png
│ │ │ ├── mipmap-hdpi
│ │ │ │ └── ic_launcher.png
│ │ │ ├── mipmap-mdpi
│ │ │ │ └── ic_launcher.png
│ │ │ ├── mipmap-xxxhdpi
│ │ │ │ └── ic_launcher.png
│ │ │ ├── values-w820dp
│ │ │ │ └── dimens.xml
│ │ │ └── layout
│ │ │ │ ├── activity_sensor_list.xml
│ │ │ │ ├── activity_image_view.xml
│ │ │ │ ├── activity_map_view.xml
│ │ │ │ └── activity_main.xml
│ │ ├── java
│ │ │ └── net
│ │ │ │ └── winsion
│ │ │ │ └── www
│ │ │ │ └── indooratlasdemo
│ │ │ │ ├── view
│ │ │ │ ├── PointEvaluator.java
│ │ │ │ └── BlueDotView.java
│ │ │ │ ├── bean
│ │ │ │ └── Point.java
│ │ │ │ ├── SensorListActivity.java
│ │ │ │ ├── utils
│ │ │ │ └── CommonMethord.java
│ │ │ │ ├── TestData.java
│ │ │ │ ├── MainActivity.java
│ │ │ │ ├── ImageViewActivity.java
│ │ │ │ └── MapViewActivity.java
│ │ └── AndroidManifest.xml
│ ├── test
│ │ └── java
│ │ │ └── net
│ │ │ └── winsion
│ │ │ └── www
│ │ │ └── indooratlasdemo
│ │ │ └── ExampleUnitTest.java
│ └── androidTest
│ │ └── java
│ │ └── net
│ │ └── winsion
│ │ └── www
│ │ └── indooratlasdemo
│ │ └── ApplicationTest.java
├── proguard-rules.pro
└── build.gradle
├── MapViewLibrary
├── .gitignore
├── src
│ └── main
│ │ ├── res
│ │ ├── values
│ │ │ ├── strings.xml
│ │ │ └── colors.xml
│ │ ├── mipmap-xxhdpi
│ │ │ ├── mark.png
│ │ │ └── compass.png
│ │ └── mipmap-hdpi
│ │ │ ├── end_point.png
│ │ │ ├── mark_touch.png
│ │ │ └── start_point.png
│ │ ├── AndroidManifest.xml
│ │ └── java
│ │ └── com
│ │ └── onlylemi
│ │ └── mapview
│ │ └── library
│ │ ├── MapViewListener.java
│ │ ├── utils
│ │ ├── ThreadHelper.java
│ │ ├── math
│ │ │ ├── TSPNearestNeighbour.java
│ │ │ ├── FloydAlgorithm.java
│ │ │ └── GeneticAlgorithm.java
│ │ ├── MapMath.java
│ │ └── MapUtils.java
│ │ ├── layer
│ │ ├── MapBaseLayer.java
│ │ ├── BitmapLayer.java
│ │ ├── RouteLayer.java
│ │ ├── MapLayer.java
│ │ ├── LocationLayer.java
│ │ └── MarkLayer.java
│ │ └── MapView.java
├── build.gradle
└── proguard-rules.pro
├── settings.gradle
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── .gitignore
├── README.md
├── gradle.properties
├── gradlew.bat
└── gradlew
/.idea/.name:
--------------------------------------------------------------------------------
1 | IndoorAtlasDemo
--------------------------------------------------------------------------------
/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/MapViewLibrary/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app', ':MapViewLibrary'
2 |
--------------------------------------------------------------------------------
/.idea/copyright/profiles_settings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | IndoorAtlasDemo
3 |
4 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dys1715/IndoorAtlasDemo/HEAD/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/MapViewLibrary/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | MapViewLibrary
3 |
4 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.iml
2 | .gradle
3 | /local.properties
4 | /.idea/workspace.xml
5 | /.idea/libraries
6 | .DS_Store
7 | /build
8 | /captures
9 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/compass.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dys1715/IndoorAtlasDemo/HEAD/app/src/main/res/mipmap-xhdpi/compass.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/compass.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dys1715/IndoorAtlasDemo/HEAD/app/src/main/res/mipmap-xxhdpi/compass.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dys1715/IndoorAtlasDemo/HEAD/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dys1715/IndoorAtlasDemo/HEAD/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dys1715/IndoorAtlasDemo/HEAD/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dys1715/IndoorAtlasDemo/HEAD/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dys1715/IndoorAtlasDemo/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/MapViewLibrary/src/main/res/mipmap-xxhdpi/mark.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dys1715/IndoorAtlasDemo/HEAD/MapViewLibrary/src/main/res/mipmap-xxhdpi/mark.png
--------------------------------------------------------------------------------
/MapViewLibrary/src/main/res/mipmap-hdpi/end_point.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dys1715/IndoorAtlasDemo/HEAD/MapViewLibrary/src/main/res/mipmap-hdpi/end_point.png
--------------------------------------------------------------------------------
/MapViewLibrary/src/main/res/mipmap-hdpi/mark_touch.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dys1715/IndoorAtlasDemo/HEAD/MapViewLibrary/src/main/res/mipmap-hdpi/mark_touch.png
--------------------------------------------------------------------------------
/MapViewLibrary/src/main/res/mipmap-xxhdpi/compass.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dys1715/IndoorAtlasDemo/HEAD/MapViewLibrary/src/main/res/mipmap-xxhdpi/compass.png
--------------------------------------------------------------------------------
/MapViewLibrary/src/main/res/mipmap-hdpi/start_point.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dys1715/IndoorAtlasDemo/HEAD/MapViewLibrary/src/main/res/mipmap-hdpi/start_point.png
--------------------------------------------------------------------------------
/.idea/encodings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/values/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 16dp
4 | 16dp
5 |
6 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Mon Dec 28 10:00:20 PST 2015
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-2.10-all.zip
7 |
--------------------------------------------------------------------------------
/.idea/inspectionProfiles/profiles_settings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #3F51B5
4 | #303F9F
5 | #FF4081
6 | #ff00a5f5
7 | #3809c5f1
8 |
9 |
--------------------------------------------------------------------------------
/MapViewLibrary/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
4 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/app/src/main/res/values-w820dp/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 64dp
6 |
7 |
--------------------------------------------------------------------------------
/app/src/test/java/net/winsion/www/indooratlasdemo/ExampleUnitTest.java:
--------------------------------------------------------------------------------
1 | package net.winsion.www.indooratlasdemo;
2 |
3 | import org.junit.Test;
4 |
5 | import static org.junit.Assert.*;
6 |
7 | /**
8 | * To work on unit tests, switch the Test Artifact in the Build Variants view.
9 | */
10 | public class ExampleUnitTest {
11 | @Test
12 | public void addition_isCorrect() throws Exception {
13 | assertEquals(4, 2 + 2);
14 | }
15 | }
--------------------------------------------------------------------------------
/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/MapViewLibrary/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #3F51B5
4 | #303F9F
5 | #FF4081
6 | #FFFFFF
7 | #ff00a5f5
8 | #3809c5f1
9 | #FF909090
10 |
11 |
--------------------------------------------------------------------------------
/MapViewLibrary/src/main/java/com/onlylemi/mapview/library/MapViewListener.java:
--------------------------------------------------------------------------------
1 | package com.onlylemi.mapview.library;
2 |
3 | /**
4 | * MapViewListener
5 | *
6 | * @author: onlylemi
7 | */
8 | public interface MapViewListener {
9 |
10 | /**
11 | * when mapview load complete to callback
12 | */
13 | void onMapLoadSuccess();
14 |
15 | /**
16 | * when mapview load error to callback
17 | */
18 | void onMapLoadFail();
19 | }
20 |
--------------------------------------------------------------------------------
/app/src/androidTest/java/net/winsion/www/indooratlasdemo/ApplicationTest.java:
--------------------------------------------------------------------------------
1 | package net.winsion.www.indooratlasdemo;
2 |
3 | import android.app.Application;
4 | import android.test.ApplicationTestCase;
5 |
6 | /**
7 | * Testing Fundamentals
8 | */
9 | public class ApplicationTest extends ApplicationTestCase {
10 | public ApplicationTest() {
11 | super(Application.class);
12 | }
13 | }
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # IndoorAtlasDemo
2 | Indooratlas室内地磁定位demo
3 | 展示了如何从云端服务器拉取数据并在本地描点功能等其他sdk功能
4 |
5 | API文档地址:http://docs.indooratlas.com/android/2.1.0/
6 |
7 | 使用过程中注意:
8 | ----------
9 | ###采集:
10 | 1.在网页上进行楼层图添加时,图片的比例一定要精确!录入的图片长宽最好是和实际长宽一致;
11 | 2.在使用采集器采集地磁数据时,路径勾画一定要规整,出现重复的路径最好完全重合覆盖住。
12 | 在行走采集到的过程中,一定要保持匀速前进,移动速度保持在0.5-0.75m/步为佳,移动时一定要保证直线,尽量不左右偏移;
13 | 3.无障碍开放空间内规划采集路径时,应该尽量多的覆盖,数据采集范围大约为手机中心点1m半径;
14 | 4.采集时保证手机屏幕朝上,平握
15 | ###定位:
16 | 1.在使用定位时,也需要保证屏幕朝上,手机平握。如果手机随手臂甩动,会造成定位点偏移,精度下降
17 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_sensor_list.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
11 |
--------------------------------------------------------------------------------
/.idea/modules.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/MapViewLibrary/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.library'
2 |
3 | android {
4 | compileSdkVersion 23
5 | buildToolsVersion "23.0.3"
6 |
7 | defaultConfig {
8 | minSdkVersion 15
9 | targetSdkVersion 23
10 | versionCode 1
11 | versionName "1.0"
12 | }
13 | buildTypes {
14 | release {
15 | minifyEnabled false
16 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
17 | }
18 | }
19 | }
20 |
21 | dependencies {
22 | compile fileTree(dir: 'libs', include: ['*.jar'])
23 | }
24 |
--------------------------------------------------------------------------------
/.idea/runConfigurations.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
11 |
12 |
--------------------------------------------------------------------------------
/app/src/main/res/values/config.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
5 |
6 | f24b095c-743c-4f10-832f-0f14d7785bb6
7 |
8 |
9 | 23d425b5-0af4-4a9a-bb9c-66b1d7df6b09
10 |
11 |
12 | c3c3b4ab-d78c-4674-8dd9-d53613f715d7
13 |
14 |
15 |
--------------------------------------------------------------------------------
/MapViewLibrary/src/main/java/com/onlylemi/mapview/library/utils/ThreadHelper.java:
--------------------------------------------------------------------------------
1 | package com.onlylemi.mapview.library.utils;
2 |
3 | import java.util.concurrent.ExecutorService;
4 | import java.util.concurrent.Executors;
5 | import java.util.concurrent.ThreadPoolExecutor;
6 |
7 | /**
8 | * Created by dys on 2016/5/23 0023.
9 | */
10 | public class ThreadHelper {
11 | private static ExecutorService threadPool;
12 |
13 | static {
14 | threadPool = Executors.newFixedThreadPool(5);
15 | // threadPool = Executors.newCachedThreadPool();
16 | // threadPool = Executors.newSingleThreadExecutor();
17 | }
18 |
19 | public static ExecutorService getThreadPool() {
20 | return threadPool;
21 | }
22 |
23 | }
--------------------------------------------------------------------------------
/app/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # By default, the flags in this file are appended to flags specified
3 | # in C:\Users\D\AppData\Local\Android\Sdk/tools/proguard/proguard-android.txt
4 | # You can edit the include path and order by changing the proguardFiles
5 | # directive in build.gradle.
6 | #
7 | # For more details, see
8 | # http://developer.android.com/guide/developing/tools/proguard.html
9 |
10 | # Add any project specific keep options here:
11 |
12 | # If your project uses WebView with JS, uncomment the following
13 | # and specify the fully qualified class name to the JavaScript interface
14 | # class:
15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
16 | # public *;
17 | #}
18 |
--------------------------------------------------------------------------------
/MapViewLibrary/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # By default, the flags in this file are appended to flags specified
3 | # in E:\eclipse\sdk\android-sdk-windows/tools/proguard/proguard-android.txt
4 | # You can edit the include path and order by changing the proguardFiles
5 | # directive in build.gradle.
6 | #
7 | # For more details, see
8 | # http://developer.android.com/guide/developing/tools/proguard.html
9 |
10 | # Add any project specific keep options here:
11 |
12 | # If your project uses WebView with JS, uncomment the following
13 | # and specify the fully qualified class name to the JavaScript interface
14 | # class:
15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
16 | # public *;
17 | #}
18 |
--------------------------------------------------------------------------------
/app/src/main/java/net/winsion/www/indooratlasdemo/view/PointEvaluator.java:
--------------------------------------------------------------------------------
1 | package net.winsion.www.indooratlasdemo.view;
2 |
3 | import android.animation.TypeEvaluator;
4 | import android.graphics.PointF;
5 |
6 | /**
7 | * Created by D on 2016/5/6 0006.
8 | * 小点移动操作
9 | */
10 | public class PointEvaluator implements TypeEvaluator {
11 | @Override
12 | public Object evaluate(float fraction, Object startValue, Object endValue) {
13 | PointF startPoint = (PointF) startValue;
14 | PointF endPoint = (PointF) endValue;
15 | float x = startPoint.x + fraction * (endPoint.x - startPoint.x);
16 | float y = startPoint.y + fraction * (endPoint.y - startPoint.y);
17 | PointF point = new PointF(x, y);
18 | return point;
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/.idea/compiler.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 |
3 | # IDE (e.g. Android Studio) users:
4 | # Gradle settings configured through the IDE *will override*
5 | # any settings specified in this file.
6 |
7 | # For more details on how to configure your build environment visit
8 | # http://www.gradle.org/docs/current/userguide/build_environment.html
9 |
10 | # Specifies the JVM arguments used for the daemon process.
11 | # The setting is particularly useful for tweaking memory settings.
12 | # Default value: -Xmx10248m -XX:MaxPermSize=256m
13 | # org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
14 |
15 | # When configured, Gradle will run in incubating parallel mode.
16 | # This option should only be used with decoupled projects. More details, visit
17 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
18 | # org.gradle.parallel=true
19 |
--------------------------------------------------------------------------------
/.idea/gradle.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/app/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.application'
2 |
3 | android {
4 | compileSdkVersion 23
5 | buildToolsVersion "23.0.3"
6 |
7 | defaultConfig {
8 | applicationId "net.winsion.www.indooratlasdemo"
9 | minSdkVersion 15
10 | targetSdkVersion 23
11 | versionCode 1
12 | versionName "1.0"
13 | }
14 | buildTypes {
15 | release {
16 | minifyEnabled false
17 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
18 | }
19 | }
20 | }
21 |
22 | repositories{
23 | maven {
24 | url "http://indooratlas-ltd.bintray.com/mvn-public"
25 | }
26 | }
27 |
28 | dependencies {
29 | compile fileTree(dir: 'libs', include: ['*.jar'])
30 | testCompile 'junit:junit:4.12'
31 | compile project(':MapViewLibrary')
32 | compile 'com.android.support:appcompat-v7:23.3.0'
33 | compile 'com.squareup.picasso:picasso:2.5.2'
34 | compile 'com.davemorrissey.labs:subsampling-scale-image-view:3.2.0'
35 | compile 'com.orhanobut:logger:1.11'
36 | compile 'com.indooratlas.android:indooratlas-android-sdk:2.1.2@aar'
37 | compile 'com.anthonycr.grant:permissions:1.0'
38 | }
39 |
--------------------------------------------------------------------------------
/.idea/inspectionProfiles/Project_Default.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/app/src/main/java/net/winsion/www/indooratlasdemo/bean/Point.java:
--------------------------------------------------------------------------------
1 | package net.winsion.www.indooratlasdemo.bean;
2 |
3 | /**
4 | * Created by D on 2016/4/29 0029.
5 | */
6 | public class Point {
7 | private float pointX;
8 | private float pointY;
9 | private double latitude;
10 | private double longitude;
11 |
12 | public Point(float pointX, float pointY) {
13 | this.pointX = pointX;
14 | this.pointY = pointY;
15 | }
16 |
17 | public Point(float pointX, float pointY, double latitude, double longitude) {
18 | this.pointX = pointX;
19 | this.pointY = pointY;
20 | this.latitude = latitude;
21 | this.longitude = longitude;
22 | }
23 |
24 | public double getLatitude() {
25 | return latitude;
26 | }
27 |
28 | public void setLatitude(double latitude) {
29 | this.latitude = latitude;
30 | }
31 |
32 | public double getLongitude() {
33 | return longitude;
34 | }
35 |
36 | public void setLongitude(double longitude) {
37 | this.longitude = longitude;
38 | }
39 |
40 | public float getPointX() {
41 | return pointX;
42 | }
43 |
44 | public void setPointX(float pointX) {
45 | this.pointX = pointX;
46 | }
47 |
48 | public float getPointY() {
49 | return pointY;
50 | }
51 |
52 | public void setPointY(float pointY) {
53 | this.pointY = pointY;
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_image_view.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
11 |
12 |
17 |
18 |
25 |
26 |
33 |
34 |
40 |
41 |
--------------------------------------------------------------------------------
/MapViewLibrary/src/main/java/com/onlylemi/mapview/library/layer/MapBaseLayer.java:
--------------------------------------------------------------------------------
1 | package com.onlylemi.mapview.library.layer;
2 |
3 | import android.graphics.Canvas;
4 | import android.graphics.Matrix;
5 | import android.util.TypedValue;
6 | import android.view.MotionEvent;
7 |
8 | import com.onlylemi.mapview.library.MapView;
9 |
10 | /**
11 | * MapBaseLayer
12 | *
13 | * @author: onlylemi
14 | */
15 | public abstract class MapBaseLayer {
16 |
17 | // map layer level
18 | protected static final int MAP_LEVEL = 0;
19 | // location layer level
20 | protected static final int LOCATION_LEVEL = Integer.MAX_VALUE;
21 |
22 | // layer show level
23 | public int level;
24 | // layer is/not show
25 | public boolean isVisible = true;
26 |
27 | protected MapView mapView;
28 |
29 | public MapBaseLayer(MapView mapView) {
30 | this.mapView = mapView;
31 | }
32 |
33 | /**
34 | * touch event
35 | *
36 | * @param event
37 | */
38 | public abstract void onTouch(MotionEvent event);
39 |
40 | /**
41 | * draw event
42 | *
43 | * @param canvas
44 | * @param currentMatrix
45 | * @param currentZoom
46 | * @param currentRotateDegrees
47 | */
48 | public abstract void draw(Canvas canvas, Matrix currentMatrix, float currentZoom,
49 | float currentRotateDegrees);
50 |
51 | public void setLevel(int level) {
52 | this.level = level;
53 | }
54 |
55 | protected float setValue(float value) {
56 | return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, value, mapView.getResources()
57 | .getDisplayMetrics());
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_map_view.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
14 |
15 |
20 |
21 |
28 |
29 |
36 |
37 |
43 |
44 |
--------------------------------------------------------------------------------
/MapViewLibrary/src/main/java/com/onlylemi/mapview/library/utils/math/TSPNearestNeighbour.java:
--------------------------------------------------------------------------------
1 | package com.onlylemi.mapview.library.utils.math;
2 |
3 | import java.net.FileNameMap;
4 | import java.util.ArrayList;
5 | import java.util.List;
6 | import java.util.Stack;
7 |
8 | public class TSPNearestNeighbour {
9 |
10 | private static final float INF = Float.MAX_VALUE;
11 |
12 | private int numberOfNodes;
13 | private Stack stack;
14 | private List list;
15 |
16 | public static TSPNearestNeighbour getInstance() {
17 | return TSPNearestNeighbourHolder.instance;
18 | }
19 |
20 | private static class TSPNearestNeighbourHolder {
21 | private static TSPNearestNeighbour instance = new TSPNearestNeighbour();
22 | }
23 |
24 | public TSPNearestNeighbour() {
25 | stack = new Stack<>();
26 | list = new ArrayList<>();
27 | }
28 |
29 | public List tsp(float matrix[][]) {
30 | numberOfNodes = matrix[0].length;
31 | int[] visited = new int[numberOfNodes];
32 | visited[0] = 1;
33 | stack.push(0);
34 | int element, dst = 0, i;
35 | boolean minFlag = false;
36 |
37 | // System.out.print(0 + "\t");
38 | list.add(0);
39 | float min = INF;
40 | while (!stack.isEmpty()) {
41 | element = stack.peek();
42 | i = 0;
43 | min = INF;
44 | while (i < numberOfNodes) {
45 | if (matrix[element][i] < INF && visited[i] == 0) {
46 | if (min > matrix[element][i]) {
47 | min = matrix[element][i];
48 | dst = i;
49 | minFlag = true;
50 | }
51 | }
52 | i++;
53 | }
54 | if (minFlag) {
55 | visited[dst] = 1;
56 | stack.push(dst);
57 | // System.out.print(dst + "\t");
58 | list.add(dst);
59 | minFlag = false;
60 | continue;
61 | }
62 | stack.pop();
63 | }
64 | return list;
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
--------------------------------------------------------------------------------
/MapViewLibrary/src/main/java/com/onlylemi/mapview/library/utils/math/FloydAlgorithm.java:
--------------------------------------------------------------------------------
1 | package com.onlylemi.mapview.library.utils.math;
2 |
3 | import java.util.ArrayList;
4 | import java.util.List;
5 |
6 | /**
7 | * FloydAlgorithm
8 | *
9 | * @author: onlylemi
10 | */
11 | public final class FloydAlgorithm {
12 |
13 | private static final int INF = Integer.MAX_VALUE;
14 | private float[][] dist;
15 |
16 | // the shortest path from i to j
17 | private int[][] path;
18 | private List result;
19 |
20 | public static FloydAlgorithm getInstance() {
21 | return FloydAlgorithmHolder.instance;
22 | }
23 |
24 | private static class FloydAlgorithmHolder {
25 | private static FloydAlgorithm instance = new FloydAlgorithm();
26 | }
27 |
28 | private void init(float[][] matrix) {
29 | dist = null;
30 | path = null;
31 | result = new ArrayList<>();
32 |
33 | this.dist = new float[matrix.length][matrix.length];
34 | this.path = new int[matrix.length][matrix.length];
35 | }
36 |
37 | /**
38 | * the shortest between begin to end
39 | *
40 | * @param begin
41 | * @param end
42 | * @param matrix
43 | */
44 | public List findCheapestPath(int begin, int end, float[][] matrix) {
45 | init(matrix);
46 |
47 | floyd(matrix);
48 | result.add(begin);
49 | findPath(begin, end);
50 | result.add(end);
51 |
52 | return result;
53 | }
54 |
55 | private void findPath(int i, int j) {
56 | int k = path[i][j];
57 | if (k == -1)
58 | return;
59 | findPath(i, k); // recursion
60 | result.add(k);
61 | findPath(k, j);
62 | }
63 |
64 | private void floyd(float[][] matrix) {
65 | int size = matrix.length;
66 | // initialize dist and path
67 | for (int i = 0; i < size; i++) {
68 | for (int j = 0; j < size; j++) {
69 | path[i][j] = -1;
70 | dist[i][j] = matrix[i][j];
71 | }
72 | }
73 | for (int k = 0; k < size; k++) {
74 | for (int i = 0; i < size; i++) {
75 | for (int j = 0; j < size; j++) {
76 | if (dist[i][k] != INF && dist[k][j] != INF
77 | && dist[i][k] + dist[k][j] < dist[i][j]) {
78 | dist[i][j] = dist[i][k] + dist[k][j];
79 | path[i][j] = k;
80 | }
81 | }
82 | }
83 | }
84 |
85 | }
86 |
87 | }
88 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @if "%DEBUG%" == "" @echo off
2 | @rem ##########################################################################
3 | @rem
4 | @rem Gradle startup script for Windows
5 | @rem
6 | @rem ##########################################################################
7 |
8 | @rem Set local scope for the variables with windows NT shell
9 | if "%OS%"=="Windows_NT" setlocal
10 |
11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
12 | set DEFAULT_JVM_OPTS=
13 |
14 | set DIRNAME=%~dp0
15 | if "%DIRNAME%" == "" set DIRNAME=.
16 | set APP_BASE_NAME=%~n0
17 | set APP_HOME=%DIRNAME%
18 |
19 | @rem Find java.exe
20 | if defined JAVA_HOME goto findJavaFromJavaHome
21 |
22 | set JAVA_EXE=java.exe
23 | %JAVA_EXE% -version >NUL 2>&1
24 | if "%ERRORLEVEL%" == "0" goto init
25 |
26 | echo.
27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28 | echo.
29 | echo Please set the JAVA_HOME variable in your environment to match the
30 | echo location of your Java installation.
31 |
32 | goto fail
33 |
34 | :findJavaFromJavaHome
35 | set JAVA_HOME=%JAVA_HOME:"=%
36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37 |
38 | if exist "%JAVA_EXE%" goto init
39 |
40 | echo.
41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42 | echo.
43 | echo Please set the JAVA_HOME variable in your environment to match the
44 | echo location of your Java installation.
45 |
46 | goto fail
47 |
48 | :init
49 | @rem Get command-line arguments, handling Windowz variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 | if "%@eval[2+2]" == "4" goto 4NT_args
53 |
54 | :win9xME_args
55 | @rem Slurp the command line arguments.
56 | set CMD_LINE_ARGS=
57 | set _SKIP=2
58 |
59 | :win9xME_args_slurp
60 | if "x%~1" == "x" goto execute
61 |
62 | set CMD_LINE_ARGS=%*
63 | goto execute
64 |
65 | :4NT_args
66 | @rem Get arguments from the 4NT Shell from JP Software
67 | set CMD_LINE_ARGS=%$
68 |
69 | :execute
70 | @rem Setup the command line
71 |
72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
73 |
74 | @rem Execute Gradle
75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
76 |
77 | :end
78 | @rem End local scope for the variables with windows NT shell
79 | if "%ERRORLEVEL%"=="0" goto mainEnd
80 |
81 | :fail
82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
83 | rem the _cmd.exe /c_ return code!
84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
85 | exit /b 1
86 |
87 | :mainEnd
88 | if "%OS%"=="Windows_NT" endlocal
89 |
90 | :omega
91 |
--------------------------------------------------------------------------------
/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
9 |
12 |
15 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
56 |
59 |
60 |
61 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
11 |
12 |
19 |
20 |
29 |
30 |
39 |
40 |
47 |
48 |
54 |
61 |
69 |
76 |
77 |
--------------------------------------------------------------------------------
/MapViewLibrary/src/main/java/com/onlylemi/mapview/library/layer/BitmapLayer.java:
--------------------------------------------------------------------------------
1 | package com.onlylemi.mapview.library.layer;
2 |
3 | import android.graphics.Bitmap;
4 | import android.graphics.BitmapFactory;
5 | import android.graphics.Canvas;
6 | import android.graphics.Matrix;
7 | import android.graphics.Paint;
8 | import android.graphics.PointF;
9 | import android.util.Log;
10 | import android.view.MotionEvent;
11 |
12 | import com.onlylemi.mapview.library.MapView;
13 |
14 | import java.util.Arrays;
15 |
16 | /**
17 | * BitmapLayer
18 | *
19 | * @author: onlylemi
20 | */
21 | public class BitmapLayer extends MapBaseLayer {
22 |
23 | private PointF location;
24 | private Bitmap bitmap;
25 | private Paint paint;
26 |
27 | private boolean autoScale = false;
28 |
29 | private OnBitmapClickListener onBitmapClickListener;
30 |
31 | public BitmapLayer(MapView mapView, Bitmap bitmap) {
32 | this(mapView, bitmap, null);
33 | }
34 |
35 | public BitmapLayer(MapView mapView, Bitmap bitmap, PointF location) {
36 | super(mapView);
37 | this.location = location;
38 | this.bitmap = bitmap;
39 |
40 | paint = new Paint();
41 | }
42 |
43 | @Override
44 | public void onTouch(MotionEvent event) {
45 | if (onBitmapClickListener != null) {
46 | float[] goal = mapView.convertMapXYToScreenXY(event.getX(), event.getY());
47 | Log.i("BitmapLayer", "goal: " + goal[0] + ", " + goal[1]);
48 | if (goal[0] > location.x - bitmap.getWidth() / 2 &&
49 | goal[0] < location.x + bitmap.getWidth() / 2 &&
50 | goal[1] > location.y - bitmap.getHeight() / 2 &&
51 | goal[1] < location.y + bitmap.getHeight() / 2) {
52 | onBitmapClickListener.onBitmapClick(this);
53 | }
54 | }
55 | }
56 |
57 | @Override
58 | public void draw(Canvas canvas, Matrix currentMatrix, float currentZoom, float
59 | currentRotateDegrees) {
60 | if (isVisible && bitmap != null) {
61 | canvas.save();
62 | float goal[] = {location.x, location.y};
63 | if (!autoScale) {
64 | currentMatrix.mapPoints(goal);
65 | } else {
66 | canvas.setMatrix(currentMatrix);
67 | }
68 | canvas.drawBitmap(bitmap, goal[0] - bitmap.getWidth() / 2,
69 | goal[1] - bitmap.getHeight() / 2, paint);
70 | canvas.restore();
71 | }
72 | }
73 |
74 | public PointF getLocation() {
75 | return location;
76 | }
77 |
78 | public void setLocation(PointF location) {
79 | this.location = location;
80 | }
81 |
82 | public Bitmap getBitmap() {
83 | return bitmap;
84 | }
85 |
86 | public void setBitmap(Bitmap bitmap) {
87 | this.bitmap = bitmap;
88 | }
89 |
90 | public void setAutoScale(boolean autoScale) {
91 | this.autoScale = autoScale;
92 | }
93 |
94 | public boolean isAutoScale() {
95 | return autoScale;
96 | }
97 |
98 | public void setOnBitmapClickListener(OnBitmapClickListener onBitmapClickListener) {
99 | this.onBitmapClickListener = onBitmapClickListener;
100 | }
101 |
102 | public interface OnBitmapClickListener {
103 | void onBitmapClick(BitmapLayer layer);
104 | }
105 | }
106 |
--------------------------------------------------------------------------------
/app/src/main/java/net/winsion/www/indooratlasdemo/SensorListActivity.java:
--------------------------------------------------------------------------------
1 | package net.winsion.www.indooratlasdemo;
2 |
3 | import android.app.Activity;
4 | import android.content.Context;
5 | import android.hardware.Sensor;
6 | import android.hardware.SensorManager;
7 | import android.os.Bundle;
8 | import android.text.method.ScrollingMovementMethod;
9 | import android.widget.TextView;
10 |
11 | import net.winsion.www.indooratlasdemo.utils.CommonMethord;
12 |
13 | import java.util.List;
14 |
15 | /**
16 | * Created by dys on 2016/5/30 0030.
17 | * 查看传感器列表
18 | */
19 | public class SensorListActivity extends Activity {
20 |
21 | private TextView mTextView;
22 | @Override
23 | protected void onCreate(Bundle savedInstanceState) {
24 | super.onCreate(savedInstanceState);
25 | setContentView(R.layout.activity_sensor_list);
26 | mTextView = (TextView)findViewById(R.id.tv_sensors);
27 | mTextView.setMovementMethod(ScrollingMovementMethod.getInstance());
28 | getSensorList();
29 | }
30 |
31 | private void getSensorList() {
32 | // 获取全部传感器列表
33 | List sensors = CommonMethord.getSensorLists(this);
34 |
35 | // 打印每个传感器信息
36 | StringBuilder strLog = new StringBuilder();
37 | int iIndex = 1;
38 | for (Sensor item : sensors) {
39 | strLog.append(iIndex + ".");
40 | strLog.append(" Sensor Type - " + item.getType() + "\r\n");
41 | strLog.append(" Sensor Name - " + item.getName() + "\r\n");
42 | strLog.append(" Sensor Version - " + item.getVersion() + "\r\n");
43 | strLog.append(" Sensor Vendor(供应商) - " + item.getVendor() + "\r\n");
44 | strLog.append(" Maximum Range(最大范围) - " + item.getMaximumRange() + "\r\n");
45 | strLog.append(" Minimum Delay(延迟) - " + item.getMinDelay() + "\r\n");
46 | strLog.append(" Power(mA) - " + item.getPower() + "\r\n");
47 | strLog.append(" Resolution(分辨率) - " + item.getResolution() + "\r\n");
48 | strLog.append("\r\n");
49 | iIndex++;
50 | }
51 | mTextView.setText(strLog);
52 | }
53 | }
54 | /* TYPE_ACCELEROMETER
55 | 1:加速度传感器,单位是m/s2,测量应用于设备X、Y、Z轴上的加速度
56 |
57 | TYPE_MAGNETIC_FIELD
58 | 2:磁力传感器,单位是uT(微特斯拉),测量设备周围三个物理轴(x,y,z)的磁场
59 |
60 | TYPE_ORIENTATION
61 | 3:方向传感器,测量设备围绕三个物理轴(x,y,z)的旋转角度
62 |
63 | TYPE_GYROSCOPE
64 | 4:陀螺仪传感器,单位是rad/s,测量设备x、y、z三轴的角加速度
65 |
66 | TYPE_LIGHT
67 | 5:光线感应传感器,单位lx,检测周围的光线强度
68 |
69 | TYPE_PRESSURE
70 | 6:压力传感器,单位是hPa(百帕斯卡),返回当前环境下的压强
71 |
72 | TYPE_TEMPERATURE
73 | 7:温度传感器,目前已被TYPE_AMBIENT_TEMPERATURE替代
74 |
75 | TYPE_PROXIMITY
76 | 8:距离传感器,单位是cm,用来测量某个对象到屏幕的距离
77 |
78 | TYPE_GRAVITY
79 | 9:重力传感器,单位是m/s2,测量应用于设备X、Y、Z轴上的重力
80 |
81 | TYPE_LINEAR_ACCELERATION
82 | 10:线性加速度传感器,单位是m/s2,该传感器是获取加速度传感器去除重力的影响得到的数据
83 |
84 | TYPE_ROTATION_VECTOR
85 | 11:旋转矢量传感器,旋转矢量代表设备的方向
86 |
87 | TYPE_RELATIVE_HUMIDITY
88 | 12:湿度传感器,单位是%,来测量周围环境的相对湿度
89 |
90 | TYPE_AMBIENT_TEMPERATURE
91 | 13:温度传感器,单位是℃
92 |
93 | TYPE_MAGNETIC_FIELD_UNCALIBRATED
94 | 14:未校准磁力传感器,提供原始的,未校准的磁场数据
95 |
96 | TYPE_GAME_ROTATION_VECTOR
97 | 15:游戏动作传感器,不收电磁干扰影响
98 |
99 | TYPE_GYROSCOPE_UNCALIBRATED
100 | 16:未校准陀螺仪传感器,提供原始的,未校准、补偿的陀螺仪数据,用于后期处理和融合定位数据
101 |
102 | TYPE_SIGNIFICANT_MOTION
103 | 17:特殊动作触发传感器
104 |
105 | TYPE_STEP_DETECTOR
106 | 18:步行检测传感器,用户每走一步就触发一次事件
107 |
108 | TYPE_STEP_COUNTER
109 | 19:计步传感器
110 |
111 | TYPE_GEOMAGNETIC_ROTATION_VECTOR
112 | 20:地磁旋转矢量传感器,提供手机的旋转矢量,当手机处于休眠状态时,仍可以记录设备的方位
113 |
114 | */
115 |
--------------------------------------------------------------------------------
/app/src/main/java/net/winsion/www/indooratlasdemo/utils/CommonMethord.java:
--------------------------------------------------------------------------------
1 | package net.winsion.www.indooratlasdemo.utils;
2 |
3 | import android.content.Context;
4 | import android.hardware.Sensor;
5 | import android.hardware.SensorManager;
6 | import android.os.Environment;
7 |
8 | import net.winsion.www.indooratlasdemo.bean.Point;
9 |
10 | import java.io.BufferedReader;
11 | import java.io.File;
12 | import java.io.FileInputStream;
13 | import java.io.FileOutputStream;
14 | import java.io.IOException;
15 | import java.io.InputStreamReader;
16 | import java.text.SimpleDateFormat;
17 | import java.util.Date;
18 | import java.util.List;
19 |
20 | /**
21 | * Created by D on 2016/5/3 0003.
22 | * 公共类
23 | */
24 | public class CommonMethord {
25 |
26 | public static boolean saveFile(String str) {
27 | String filePath;
28 | boolean hasSDCard = Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED);
29 | if (hasSDCard) {
30 | filePath = Environment.getExternalStorageDirectory().toString() + File.separator + "points_" + getCurrentTime() + ".txt";
31 | } else {
32 | filePath = Environment.getDownloadCacheDirectory().toString() + File.separator + "points_" + getCurrentTime() + ".txt";
33 | }
34 | try {
35 | File file = new File(filePath);
36 | if (!file.exists()) {
37 | File dir = new File(file.getParent());
38 | dir.mkdirs();
39 | file.createNewFile();
40 | }
41 | FileOutputStream outStream = new FileOutputStream(file);
42 | outStream.write(str.getBytes());
43 | outStream.close();
44 | return true;
45 | } catch (Exception e) {
46 | e.printStackTrace();
47 | return false;
48 | }
49 | }
50 |
51 | public static String getFile(String fileTime) {
52 | File file = new File(Environment.getExternalStorageDirectory(), "points_" + fileTime + ".txt");
53 | try {
54 | //读取字节流(FileInputStream)并转换为字符流(isr)
55 | InputStreamReader isr = new InputStreamReader(new FileInputStream(file), "utf-8");
56 | //创建字符流缓冲区
57 | BufferedReader bufferedReader = new BufferedReader(isr);
58 | String line;
59 | String str = "";
60 | while ((line = bufferedReader.readLine()) != null) {
61 | System.out.println(line);
62 | str += line + '\n';
63 | }
64 | isr.close();
65 | bufferedReader.close();
66 | return str;
67 | } catch (IOException e) {
68 | e.printStackTrace();
69 | return "get file failed";
70 | }
71 | }
72 |
73 | public static String ListToStr(List list) {
74 | StringBuilder str = new StringBuilder();
75 | for (int i = 0; i < list.size(); i++) {
76 | str.append(i)
77 | .append(">>[x=")
78 | .append(list.get(i).getPointX())
79 | .append(",y=")
80 | .append(list.get(i).getPointY())
81 | .append(",latitude=")
82 | .append(list.get(i).getLatitude())
83 | .append(",longitude=")
84 | .append(list.get(i).getLongitude())
85 | .append("]")
86 | .append('\n');
87 | }
88 | return str.toString();
89 | }
90 |
91 | public static String getCurrentTime() {
92 | return new SimpleDateFormat("yyyyMMddHH").format(new Date());
93 | }
94 |
95 | public static List getSensorLists(Context mContext){
96 | // 获取传感器管理器
97 | SensorManager sensorManager = (SensorManager) mContext.getSystemService(Context.SENSOR_SERVICE);
98 |
99 | // 获取全部传感器列表
100 | return sensorManager.getSensorList(Sensor.TYPE_ALL);
101 | }
102 | }
103 |
--------------------------------------------------------------------------------
/MapViewLibrary/src/main/java/com/onlylemi/mapview/library/layer/RouteLayer.java:
--------------------------------------------------------------------------------
1 | package com.onlylemi.mapview.library.layer;
2 |
3 | import android.graphics.Bitmap;
4 | import android.graphics.BitmapFactory;
5 | import android.graphics.Canvas;
6 | import android.graphics.Color;
7 | import android.graphics.Matrix;
8 | import android.graphics.Paint;
9 | import android.graphics.PointF;
10 | import android.util.Log;
11 | import android.view.MotionEvent;
12 |
13 | import com.onlylemi.mapview.library.MapView;
14 | import com.onlylemi.mapview.library.R;
15 |
16 | import java.util.List;
17 |
18 | /**
19 | * RouteLayer
20 | *
21 | * @author: onlylemi
22 | */
23 | public class RouteLayer extends MapBaseLayer {
24 |
25 | private List routeList; // routes list
26 | private List nodeList; // nodes list
27 |
28 | private float routeWidth; // the width of route
29 |
30 | private Bitmap routeStartBmp;
31 | private Bitmap routeEndBmp;
32 |
33 | private Paint paint;
34 |
35 | public RouteLayer(MapView mapView) {
36 | this(mapView, null, null);
37 | }
38 |
39 | public RouteLayer(MapView mapView, List nodeList, List routeList) {
40 | super(mapView);
41 | this.nodeList = nodeList;
42 | this.routeList = routeList;
43 |
44 | initLayer();
45 | }
46 |
47 | private void initLayer() {
48 | this.routeWidth = 10;
49 |
50 | paint = new Paint();
51 | paint.setAntiAlias(true);
52 | paint.setColor(Color.BLUE);
53 | paint.setStyle(Paint.Style.FILL_AND_STROKE);
54 |
55 | routeStartBmp = BitmapFactory.decodeResource(mapView.getResources(),
56 | R.mipmap.start_point);
57 | routeEndBmp = BitmapFactory.decodeResource(mapView.getResources(),
58 | R.mipmap.end_point);
59 | }
60 |
61 | @Override
62 | public void onTouch(MotionEvent event) {
63 |
64 | }
65 |
66 | @Override
67 | public void draw(Canvas canvas, Matrix currentMatrix, float currentZoom, float
68 | currentRotateDegrees) {
69 | if (isVisible && routeList != null && nodeList != null) {
70 | canvas.save();
71 | if (routeList.size() != 0 && nodeList.size() != 0) {
72 | // draw route
73 | for (int i = 0; i < routeList.size() - 1; i++) {
74 | // start node
75 | PointF start = nodeList.get(routeList.get(i));
76 | // end node
77 | PointF end = nodeList.get(routeList.get(i + 1));
78 |
79 | float goal1[] = {start.x, start.y};
80 | float goal2[] = {end.x, end.y};
81 | currentMatrix.mapPoints(goal1);
82 | currentMatrix.mapPoints(goal2);
83 | paint.setStrokeWidth(routeWidth);
84 | canvas.drawLine(goal1[0], goal1[1], goal2[0], goal2[1], paint);
85 | }
86 |
87 | // draw bmp
88 | float goal1[] = {nodeList.get(routeList.get(0)).x,
89 | nodeList.get(routeList.get(0)).y};
90 | float goal2[] = {
91 | nodeList.get(routeList.get(routeList.size() - 1)).x,
92 | nodeList.get(routeList.get(routeList.size() - 1)).y};
93 | currentMatrix.mapPoints(goal1);
94 | currentMatrix.mapPoints(goal2);
95 | canvas.drawBitmap(routeStartBmp,
96 | goal1[0] - routeStartBmp.getWidth() / 2, goal1[1]
97 | - routeStartBmp.getHeight(), paint);
98 | canvas.drawBitmap(routeEndBmp,
99 | goal2[0] - routeEndBmp.getWidth() / 2, goal2[1]
100 | - routeEndBmp.getHeight(), paint);
101 | }
102 | canvas.restore();
103 | }
104 | }
105 |
106 | public void setNodeList(List nodeList) {
107 | this.nodeList = nodeList;
108 | }
109 |
110 | public void setRouteList(List routeList) {
111 | this.routeList = routeList;
112 | }
113 | }
114 |
--------------------------------------------------------------------------------
/MapViewLibrary/src/main/java/com/onlylemi/mapview/library/layer/MapLayer.java:
--------------------------------------------------------------------------------
1 | package com.onlylemi.mapview.library.layer;
2 |
3 | import android.graphics.Bitmap;
4 | import android.graphics.Canvas;
5 | import android.graphics.Matrix;
6 | import android.graphics.Paint;
7 | import android.graphics.Picture;
8 | import android.util.Log;
9 | import android.view.MotionEvent;
10 | import android.view.ViewTreeObserver;
11 |
12 | import com.onlylemi.mapview.library.MapView;
13 |
14 | /**
15 | * MapLayer
16 | */
17 | public class MapLayer extends MapBaseLayer {
18 |
19 | private static final String TAG = "MapLayer";
20 |
21 | // private Picture image = null;
22 | private Bitmap mBitmap = null;
23 | private boolean hasMeasured;
24 | private Paint mPaint;
25 |
26 | public MapLayer(MapView mapView) {
27 | super(mapView);
28 | level = MAP_LEVEL;
29 | }
30 |
31 | // public void setImage(Picture image) {
32 | // this.image = image;
33 | //
34 | // if (mapView.getWidth() == 0) {
35 | // ViewTreeObserver vto = mapView.getViewTreeObserver();
36 | // vto.addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
37 | // public boolean onPreDraw() {
38 | // if (!hasMeasured) {
39 | // initMapLayer();
40 | // hasMeasured = true;
41 | // }
42 | // return true;
43 | // }
44 | // });
45 | // } else {
46 | // initMapLayer();
47 | // }
48 | // }
49 |
50 | public void setImage(Bitmap bitmap) {
51 | mBitmap = bitmap;
52 |
53 | if (mapView.getWidth() == 0) {
54 | ViewTreeObserver vto = mapView.getViewTreeObserver();
55 | vto.addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
56 | public boolean onPreDraw() {
57 | if (!hasMeasured) {
58 | initMapLayer();
59 | hasMeasured = true;
60 | }
61 | return true;
62 | }
63 | });
64 | } else {
65 | initMapLayer();
66 | }
67 | }
68 |
69 | /**
70 | * init map image layer
71 | */
72 | private void initMapLayer() {
73 | mPaint = new Paint();
74 | // float zoom = getInitZoom(mapView.getWidth(), mapView.getHeight(), image.getWidth(), image.getHeight());
75 | float zoom = getInitZoom(mapView.getWidth(), mapView.getHeight(), mBitmap.getWidth(), mBitmap.getHeight());
76 | Log.i(TAG, "zoom:" + zoom);
77 | mapView.setCurrentZoom(zoom, 0, 0);
78 |
79 | // float width = mapView.getWidth() - zoom * image.getWidth();
80 | // float height = mapView.getHeight() - zoom * image.getHeight();
81 | float width = mapView.getWidth() - zoom * mBitmap.getWidth();
82 | float height = mapView.getHeight() - zoom * mBitmap.getHeight();
83 |
84 | mapView.translate(width / 2, height / 2);
85 | }
86 |
87 | /**
88 | * calculate init zoom
89 | *
90 | * @param viewWidth
91 | * @param viewHeight
92 | * @param imageWidth
93 | * @param imageHeight
94 | * @return
95 | */
96 | private float getInitZoom(float viewWidth, float viewHeight, float imageWidth, float imageHeight) {
97 | float widthRatio = viewWidth / imageWidth;
98 | float heightRatio = viewHeight / imageHeight;
99 |
100 | Log.i(TAG, "widthRatio:" + widthRatio);
101 | Log.i(TAG, "widthRatio:" + heightRatio);
102 |
103 | if (widthRatio * imageHeight <= viewHeight) {
104 | return widthRatio;
105 | } else if (heightRatio * imageWidth <= viewWidth) {
106 | return heightRatio;
107 | }
108 | return 0;
109 | }
110 |
111 | @Override
112 | public void onTouch(MotionEvent event) {
113 |
114 | }
115 |
116 | @Override
117 | public void draw(Canvas canvas, Matrix currentMatrix, float currentZoom, float currentRotateDegrees) {
118 | canvas.save();
119 | canvas.setMatrix(currentMatrix);
120 | if (mBitmap != null) {
121 | canvas.drawBitmap(mBitmap, 0, 0, null);
122 | } else {
123 | Log.e(TAG, "mBitmap is null,load map fail");
124 | }
125 | // if (image != null) {
126 | // canvas.drawPicture(image);
127 | // }else {
128 | // Log.e(TAG,"image is null,load map fail");
129 | // }
130 | canvas.restore();
131 | }
132 |
133 | // public Picture getImage() {
134 | // return image;
135 | // }
136 |
137 | public Bitmap getImage() {
138 | return mBitmap;
139 | }
140 | }
141 |
--------------------------------------------------------------------------------
/app/src/main/java/net/winsion/www/indooratlasdemo/view/BlueDotView.java:
--------------------------------------------------------------------------------
1 | package net.winsion.www.indooratlasdemo.view;
2 |
3 | import android.content.Context;
4 | import android.graphics.Bitmap;
5 | import android.graphics.BitmapFactory;
6 | import android.graphics.Canvas;
7 | import android.graphics.Matrix;
8 | import android.graphics.Paint;
9 | import android.graphics.PointF;
10 | import android.os.Build;
11 | import android.support.annotation.NonNull;
12 | import android.util.AttributeSet;
13 | import android.util.Log;
14 | import android.util.TypedValue;
15 | import android.view.MotionEvent;
16 |
17 | import com.davemorrissey.labs.subscaleview.SubsamplingScaleImageView;
18 | import com.onlylemi.mapview.library.MapView;
19 | import com.onlylemi.mapview.library.utils.MapMath;
20 |
21 | import net.winsion.www.indooratlasdemo.R;
22 |
23 | /**
24 | * Extends great ImageView library by Dave Morrissey. See more:
25 | * https://github.com/davemorrissey/subsampling-scale-image-view.
26 | */
27 | public class BlueDotView extends SubsamplingScaleImageView {
28 |
29 | private float radius = 1.0f;
30 | private PointF dotCenter = null;
31 | private Bitmap compassIndicatorArrowBitmap; //指南针箭头
32 | private float compassIndicatorArrowRotateDegree; //箭头旋转角度
33 | private float rangeIndicatorMeters; //范围指示器
34 | private float compassIndicatorGap;
35 | private boolean drawIndicator = true;
36 |
37 | public BlueDotView(Context context) {
38 | this(context, null);
39 | }
40 |
41 | public BlueDotView(Context context, AttributeSet attr) {
42 | super(context, attr);
43 | initialise();
44 | }
45 |
46 | private void initialise() {
47 | setWillNotDraw(false);
48 | setPanLimit(SubsamplingScaleImageView.PAN_LIMIT_CENTER);
49 | compassIndicatorArrowBitmap = BitmapFactory.decodeResource(this.getContext().getResources(), R.mipmap.compass);
50 |
51 | compassIndicatorGap = setValue(15.0f);
52 | }
53 |
54 | @Override
55 | protected void onDraw(Canvas canvas) {
56 | super.onDraw(canvas);
57 | if (!isReady()) {
58 | return;
59 | }
60 | canvas.save();
61 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
62 | canvas.saveLayerAlpha(0,0,0,0,0x88);
63 | }
64 | drawDot(canvas);
65 | canvas.restore();
66 | }
67 |
68 | /**
69 | * 画定位点&方向指示器&范围指示器
70 | *
71 | * @param canvas
72 | */
73 | private void drawDot(Canvas canvas) {
74 | if (dotCenter != null) {
75 | PointF vPoint = sourceToViewCoord(dotCenter);
76 | float scaledRadius = getScale() * radius;
77 | Paint paint = new Paint();
78 | paint.setAntiAlias(true);
79 | paint.setStyle(Paint.Style.FILL);
80 | //画范围指示器
81 | paint.setColor(getResources().getColor(R.color.ia_blue_tint));
82 | canvas.drawCircle(vPoint.x, vPoint.y, scaledRadius * rangeIndicatorMeters, paint);
83 | //画定位点
84 | paint.setColor(getResources().getColor(R.color.ia_blue));
85 | canvas.drawCircle(vPoint.x, vPoint.y, scaledRadius, paint);
86 | //画箭头
87 | if (drawIndicator && compassIndicatorArrowBitmap != null) {
88 | canvas.save();
89 | canvas.rotate(getCompassIndicatorArrowRotateDegree(), vPoint.x, vPoint.y);
90 | canvas.drawBitmap(compassIndicatorArrowBitmap,
91 | vPoint.x - compassIndicatorArrowBitmap.getWidth() / 2,
92 | vPoint.y - scaledRadius - compassIndicatorGap,
93 | new Paint());
94 | canvas.restore();
95 | }
96 | }
97 | }
98 |
99 | public void setDrawIndicator(boolean drawIndicator) {
100 | this.drawIndicator = drawIndicator;
101 | }
102 |
103 | public void setRadius(float radius) {
104 | this.radius = radius;
105 | }
106 |
107 | public void setDotCenter(PointF dotCenter) {
108 | this.dotCenter = dotCenter;
109 | }
110 |
111 | protected float setValue(float value) {
112 | return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, value, getResources().getDisplayMetrics());
113 | }
114 |
115 | public void setCompassIndicatorArrowRotateDegree(float compassIndicatorArrowRotateDegree) {
116 | this.compassIndicatorArrowRotateDegree = compassIndicatorArrowRotateDegree;
117 | }
118 |
119 | public float getCompassIndicatorArrowRotateDegree() {
120 | return this.compassIndicatorArrowRotateDegree;
121 | }
122 |
123 | public void setRangeIndicatorMeters(float rangeIndicatorMeters) {
124 | this.rangeIndicatorMeters = rangeIndicatorMeters;
125 | }
126 | }
127 |
--------------------------------------------------------------------------------
/app/src/main/java/net/winsion/www/indooratlasdemo/TestData.java:
--------------------------------------------------------------------------------
1 | package net.winsion.www.indooratlasdemo;
2 |
3 | import android.graphics.PointF;
4 |
5 | import java.util.ArrayList;
6 | import java.util.List;
7 |
8 | /**
9 | * Created by dys on 2016/5/20 0020.
10 | * 标志点测试数据
11 | */
12 | public class TestData {
13 |
14 | public static List getNodesList() {
15 | List nodes = new ArrayList<>();
16 | nodes.add(new PointF(637, 153));
17 | nodes.add(new PointF(732, 153));
18 | nodes.add(new PointF(732, 107));
19 | nodes.add(new PointF(770, 153));
20 | nodes.add(new PointF(770, 205));
21 | nodes.add(new PointF(770, 240));
22 | nodes.add(new PointF(770, 275));
23 | nodes.add(new PointF(770, 120));
24 | nodes.add(new PointF(770, 90));
25 | nodes.add(new PointF(840, 153));
26 | nodes.add(new PointF(840, 205));
27 | nodes.add(new PointF(840, 240));
28 | nodes.add(new PointF(840, 275));
29 | nodes.add(new PointF(840, 120));
30 | nodes.add(new PointF(840, 90));
31 | nodes.add(new PointF(840, 50));
32 | nodes.add(new PointF(915, 153));
33 | nodes.add(new PointF(915, 205));
34 | nodes.add(new PointF(915, 240));
35 | nodes.add(new PointF(915, 275));
36 | nodes.add(new PointF(915, 120));
37 | nodes.add(new PointF(915, 90));
38 | nodes.add(new PointF(915, 50));
39 | nodes.add(new PointF(965, 153));
40 | nodes.add(new PointF(965, 185));
41 | nodes.add(new PointF(965, 277));
42 | nodes.add(new PointF(965, 77));
43 | //27
44 | return nodes;
45 | }
46 |
47 | public static List getNodesContactList() {
48 | List nodesContact = new ArrayList();
49 | nodesContact.add(new PointF(0, 1));
50 | nodesContact.add(new PointF(1, 2));
51 | nodesContact.add(new PointF(1, 3));
52 | nodesContact.add(new PointF(3, 4));
53 | nodesContact.add(new PointF(3, 7));
54 | nodesContact.add(new PointF(3, 9));
55 | nodesContact.add(new PointF(4, 5));
56 | nodesContact.add(new PointF(5, 6));
57 | nodesContact.add(new PointF(7, 8));
58 | nodesContact.add(new PointF(9, 10));
59 | nodesContact.add(new PointF(9, 13));
60 | nodesContact.add(new PointF(9, 16));
61 | nodesContact.add(new PointF(10, 11));
62 | nodesContact.add(new PointF(11, 12));
63 | nodesContact.add(new PointF(13, 14));
64 | nodesContact.add(new PointF(14, 15));
65 | nodesContact.add(new PointF(16, 17));
66 | nodesContact.add(new PointF(16, 20));
67 | nodesContact.add(new PointF(16, 23));
68 | nodesContact.add(new PointF(17, 18));
69 | nodesContact.add(new PointF(18, 19));
70 | nodesContact.add(new PointF(20, 21));
71 | nodesContact.add(new PointF(21, 22));
72 | nodesContact.add(new PointF(23, 24));
73 | nodesContact.add(new PointF(23, 26));
74 | nodesContact.add(new PointF(24, 25));
75 | //26
76 |
77 | return nodesContact;
78 | }
79 |
80 | public static List getMarks() {
81 | List marks = new ArrayList<>();
82 | marks.add(new PointF(637, 153));
83 | marks.add(new PointF(726, 113));
84 | marks.add(new PointF(770, 200));
85 | marks.add(new PointF(770, 240));
86 | marks.add(new PointF(775, 273));
87 | marks.add(new PointF(990, 187));
88 | marks.add(new PointF(990, 276));
89 | marks.add(new PointF(770, 90));
90 | marks.add(new PointF(915, 90));
91 | marks.add(new PointF(915, 50));
92 | marks.add(new PointF(835, 200));
93 | marks.add(new PointF(835, 240));
94 | marks.add(new PointF(835, 275));
95 | marks.add(new PointF(830, 120));
96 | marks.add(new PointF(830, 80));
97 | marks.add(new PointF(840, 50));
98 | return marks;
99 | }
100 |
101 | public static List getMarksName() {
102 | List marksName = new ArrayList<>();
103 | // for (int i = 0; i < getMarks().size(); i++) {
104 | // marksName.add("Shop " + (i + 1));
105 | // }
106 | marksName.add("Enter");
107 | marksName.add("饮水机");
108 | marksName.add("小明");
109 | marksName.add("=_=");
110 | marksName.add("志银");
111 | marksName.add("周聪");
112 | marksName.add("李霈");
113 | marksName.add("金樑");
114 | marksName.add("浩亮");
115 | marksName.add("是谁呢");
116 | marksName.add("昕昕");
117 | marksName.add("王蒙");
118 | marksName.add("武军");
119 | marksName.add("no body");
120 | marksName.add("金龙");
121 | marksName.add("no body+1");
122 |
123 | return marksName;
124 | }
125 | }
126 |
--------------------------------------------------------------------------------
/MapViewLibrary/src/main/java/com/onlylemi/mapview/library/layer/LocationLayer.java:
--------------------------------------------------------------------------------
1 | package com.onlylemi.mapview.library.layer;
2 |
3 | import android.graphics.Bitmap;
4 | import android.graphics.BitmapFactory;
5 | import android.graphics.Canvas;
6 | import android.graphics.Matrix;
7 | import android.graphics.Paint;
8 | import android.graphics.PointF;
9 | import android.view.MotionEvent;
10 |
11 | import com.onlylemi.mapview.library.MapView;
12 | import com.onlylemi.mapview.library.R;
13 |
14 | /**
15 | * LocationLayer
16 | */
17 | public class LocationLayer extends MapBaseLayer {
18 |
19 | private boolean openCompass = false;
20 |
21 | // compass color
22 | private static final int DEFAULT_LOCATION_COLOR = 0xFF3EBFC9; //暗青蓝色
23 | private static final int DEFAULT_LOCATION_SHADOW_COLOR = 0xFF909090; //灰色
24 | private static final int DEFAULT_INDICATOR_ARC_COLOR = 0xFFFA4A8D; //粉红色
25 | private static final int DEFAULT_INDICATOR_CIRCLE_COLOR = 0xFF00F0FF; //亮青蓝色
26 | private static final float COMPASS_DELTA_ANGLE = 5.0f; //delta angle角度增量
27 | private static final int BLUE_DOT = 0xff00a5f5; //定位点蓝色
28 | private static final int RANGE_INDICATOR = 0x3809c5f1; //范围指示器蓝色
29 |
30 | private float defaultLocationCircleRadius;
31 |
32 | private float compassIndicatorGap;
33 | private float compassIndicatorArrowRotateDegree; //箭头旋转角度
34 | private float rangeIndicatorMeters; //范围指示器
35 | private float[] goal = new float[2];
36 |
37 | private Bitmap compassIndicatorArrowBitmap; //指南针指示箭头
38 |
39 |
40 | private Paint dotPaint,rangeIndicatorPaint,compassPaint;
41 |
42 | private PointF currentPosition = null;
43 |
44 | public LocationLayer(MapView mapView) {
45 | this(mapView, null);
46 | }
47 |
48 | @Override
49 | public void onTouch(MotionEvent event) {
50 |
51 | }
52 |
53 | public LocationLayer(MapView mapView, PointF currentPosition) {
54 | this(mapView, currentPosition, true); //默认打开
55 | }
56 |
57 | public LocationLayer(MapView mapView, PointF currentPosition, boolean openCompass) {
58 | super(mapView);
59 | this.currentPosition = currentPosition;
60 | this.openCompass = openCompass;
61 |
62 | level = LOCATION_LEVEL;
63 | initLayer();
64 | }
65 |
66 | private void initLayer() {
67 | // setting dufault values
68 | defaultLocationCircleRadius = setValue(5f);
69 |
70 | // default dotPaint
71 | dotPaint = new Paint(Paint.ANTI_ALIAS_FLAG); //使位图抗锯齿的标志
72 | dotPaint.setAntiAlias(true);
73 | dotPaint.setStyle(Paint.Style.FILL);
74 | dotPaint.setColor(BLUE_DOT);
75 | //rangeIndicatorPaint
76 | rangeIndicatorPaint = new Paint(Paint.ANTI_ALIAS_FLAG); //使位图抗锯齿的标志
77 | rangeIndicatorPaint.setAntiAlias(true);
78 | rangeIndicatorPaint.setStyle(Paint.Style.FILL);
79 | rangeIndicatorPaint.setColor(RANGE_INDICATOR);
80 |
81 | compassIndicatorGap = setValue(15.0f);
82 |
83 | compassIndicatorArrowBitmap = BitmapFactory.decodeResource(mapView.getResources(), R.mipmap.compass);
84 | }
85 |
86 | @Override
87 | public void draw(Canvas canvas, Matrix currentMatrix, float currentZoom, float currentRotateDegrees) {
88 | if (isVisible && currentPosition != null) {
89 | canvas.save();
90 | goal[0] = currentPosition.x;
91 | goal[1] = currentPosition.y;
92 | currentMatrix.mapPoints(goal);
93 |
94 | if (openCompass) {
95 | //画定位点
96 | canvas.drawCircle(goal[0], goal[1], defaultLocationCircleRadius, dotPaint);
97 | //画范围指示器
98 | canvas.drawCircle(goal[0], goal[1], defaultLocationCircleRadius * rangeIndicatorMeters, rangeIndicatorPaint);
99 | //画箭头
100 | if (compassIndicatorArrowBitmap != null) {
101 | canvas.save();
102 | canvas.rotate(this.compassIndicatorArrowRotateDegree, goal[0], goal[1]);
103 | canvas.drawBitmap(compassIndicatorArrowBitmap,
104 | goal[0] - compassIndicatorArrowBitmap.getWidth() / 2,
105 | goal[1] - defaultLocationCircleRadius - compassIndicatorGap,
106 | dotPaint);
107 | canvas.restore();
108 | }
109 | }
110 | canvas.restore();
111 | }
112 | }
113 |
114 | public void setCurrentPosition(PointF currentPosition) {
115 | this.currentPosition = currentPosition;
116 | }
117 |
118 | public void setCompassIndicatorArrowRotateDegree(float compassIndicatorArrowRotateDegree) {
119 | this.compassIndicatorArrowRotateDegree = compassIndicatorArrowRotateDegree;
120 | }
121 |
122 | public void setRangeIndicatorMeters(float rangeIndicatorMeters) {
123 | this.rangeIndicatorMeters = rangeIndicatorMeters;
124 | }
125 | }
126 |
--------------------------------------------------------------------------------
/MapViewLibrary/src/main/java/com/onlylemi/mapview/library/layer/MarkLayer.java:
--------------------------------------------------------------------------------
1 | package com.onlylemi.mapview.library.layer;
2 |
3 | import android.graphics.Bitmap;
4 | import android.graphics.BitmapFactory;
5 | import android.graphics.Canvas;
6 | import android.graphics.Color;
7 | import android.graphics.Matrix;
8 | import android.graphics.Paint;
9 | import android.graphics.PointF;
10 | import android.util.Log;
11 | import android.view.MotionEvent;
12 |
13 | import com.onlylemi.mapview.library.MapView;
14 | import com.onlylemi.mapview.library.utils.MapMath;
15 | import com.onlylemi.mapview.library.R;
16 |
17 | import java.util.List;
18 |
19 | /**
20 | * MarkLayer
21 | *
22 | * @author: onlylemi
23 | */
24 | public class MarkLayer extends MapBaseLayer {
25 |
26 | private List marks;
27 | private List marksName;
28 | private MarkIsClickListener listener;
29 |
30 | private Bitmap bmpMark, bmpMarkTouch;
31 |
32 | private float radiusMark;
33 | private boolean isClickMark = false;
34 | private int num = -1;
35 |
36 | private Paint paint;
37 |
38 | public MarkLayer(MapView mapView) {
39 | this(mapView, null, null);
40 | }
41 |
42 | public MarkLayer(MapView mapView, List marks, List marksName) {
43 | super(mapView);
44 | this.marks = marks;
45 | this.marksName = marksName;
46 |
47 | initLayer();
48 | }
49 |
50 | private void initLayer() {
51 | radiusMark = setValue(10f);
52 |
53 | bmpMark = BitmapFactory.decodeResource(mapView.getResources(), R.mipmap.mark);
54 | bmpMarkTouch = BitmapFactory.decodeResource(mapView.getResources(), R.mipmap.mark_touch);
55 |
56 | paint = new Paint();
57 | paint.setAntiAlias(true);
58 | paint.setStyle(Paint.Style.FILL_AND_STROKE);
59 | }
60 |
61 | @Override
62 | public void onTouch(MotionEvent event) {
63 | if (marks != null) {
64 | if (marks.size() != 0) {
65 | float[] goal = mapView.convertMapXYToScreenXY(event.getX(), event.getY());
66 | for (int i = 0; i < marks.size(); i++) {
67 | if (MapMath.getDistanceBetweenTwoPoints(
68 | goal[0],
69 | goal[1],
70 | marks.get(i).x - bmpMark.getWidth() / 2,
71 | marks.get(i).y - bmpMark.getHeight() / 2) <= 50) {
72 | num = i;
73 | isClickMark = true;
74 | break;
75 | }
76 |
77 | if (i == marks.size() - 1) {
78 | isClickMark = false;
79 | listener.markIsClick(-1); //点击了空白区域返回-1标志
80 | }
81 | }
82 |
83 | if (listener != null && isClickMark) {
84 | listener.markIsClick(num);
85 | mapView.refresh();
86 | }
87 | }
88 | }
89 | }
90 |
91 | @Override
92 | public void draw(Canvas canvas, Matrix currentMatrix, float currentZoom, float
93 | currentRotateDegrees) {
94 | if (isVisible && marks != null) {
95 | canvas.save();
96 | if (marks.size() != 0) {
97 | for (int i = 0; i < marks.size(); i++) {
98 | PointF mark = marks.get(i);
99 | float goal[] = {mark.x, mark.y};
100 | currentMatrix.mapPoints(goal);
101 |
102 | paint.setColor(Color.BLACK);
103 | paint.setTextSize(radiusMark);
104 | //mark name
105 | if (mapView.getCurrentZoom() > 1.0 && marksName != null) {
106 | if (marksName.size() == marks.size()) {
107 | canvas.drawText(marksName.get(i), goal[0] - radiusMark, goal[1] -
108 | radiusMark / 2, paint);
109 | }
110 | }
111 | //mark ico
112 | canvas.drawBitmap(bmpMark, goal[0] - bmpMark.getWidth() / 2,
113 | goal[1] - bmpMark.getHeight() / 2, paint);
114 | if (i == num && isClickMark) {
115 | canvas.drawBitmap(bmpMarkTouch, goal[0] - bmpMarkTouch.getWidth() / 2,
116 | goal[1] - bmpMarkTouch.getHeight(), paint);
117 | }
118 | }
119 | }
120 | canvas.restore();
121 | }
122 | }
123 |
124 | public int getNum() {
125 | return num;
126 | }
127 |
128 | public void setNum(int num) {
129 | this.num = num;
130 | }
131 |
132 | public List getMarks() {
133 | return marks;
134 | }
135 |
136 | public void setMarks(List marks) {
137 | this.marks = marks;
138 | }
139 |
140 | public List getMarksName() {
141 | return marksName;
142 | }
143 |
144 | public void setMarksName(List marksName) {
145 | this.marksName = marksName;
146 | }
147 |
148 | public boolean isClickMark() {
149 | return isClickMark;
150 | }
151 |
152 | public void setMarkIsClickListener(MarkIsClickListener listener) {
153 | this.listener = listener;
154 | }
155 |
156 | public interface MarkIsClickListener {
157 | void markIsClick(int num);
158 | }
159 | }
160 |
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | ##############################################################################
4 | ##
5 | ## Gradle start up script for UN*X
6 | ##
7 | ##############################################################################
8 |
9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
10 | DEFAULT_JVM_OPTS=""
11 |
12 | APP_NAME="Gradle"
13 | APP_BASE_NAME=`basename "$0"`
14 |
15 | # Use the maximum available, or set MAX_FD != -1 to use that value.
16 | MAX_FD="maximum"
17 |
18 | warn ( ) {
19 | echo "$*"
20 | }
21 |
22 | die ( ) {
23 | echo
24 | echo "$*"
25 | echo
26 | exit 1
27 | }
28 |
29 | # OS specific support (must be 'true' or 'false').
30 | cygwin=false
31 | msys=false
32 | darwin=false
33 | case "`uname`" in
34 | CYGWIN* )
35 | cygwin=true
36 | ;;
37 | Darwin* )
38 | darwin=true
39 | ;;
40 | MINGW* )
41 | msys=true
42 | ;;
43 | esac
44 |
45 | # Attempt to set APP_HOME
46 | # Resolve links: $0 may be a link
47 | PRG="$0"
48 | # Need this for relative symlinks.
49 | while [ -h "$PRG" ] ; do
50 | ls=`ls -ld "$PRG"`
51 | link=`expr "$ls" : '.*-> \(.*\)$'`
52 | if expr "$link" : '/.*' > /dev/null; then
53 | PRG="$link"
54 | else
55 | PRG=`dirname "$PRG"`"/$link"
56 | fi
57 | done
58 | SAVED="`pwd`"
59 | cd "`dirname \"$PRG\"`/" >/dev/null
60 | APP_HOME="`pwd -P`"
61 | cd "$SAVED" >/dev/null
62 |
63 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
64 |
65 | # Determine the Java command to use to start the JVM.
66 | if [ -n "$JAVA_HOME" ] ; then
67 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
68 | # IBM's JDK on AIX uses strange locations for the executables
69 | JAVACMD="$JAVA_HOME/jre/sh/java"
70 | else
71 | JAVACMD="$JAVA_HOME/bin/java"
72 | fi
73 | if [ ! -x "$JAVACMD" ] ; then
74 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
75 |
76 | Please set the JAVA_HOME variable in your environment to match the
77 | location of your Java installation."
78 | fi
79 | else
80 | JAVACMD="java"
81 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
82 |
83 | Please set the JAVA_HOME variable in your environment to match the
84 | location of your Java installation."
85 | fi
86 |
87 | # Increase the maximum file descriptors if we can.
88 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
89 | MAX_FD_LIMIT=`ulimit -H -n`
90 | if [ $? -eq 0 ] ; then
91 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
92 | MAX_FD="$MAX_FD_LIMIT"
93 | fi
94 | ulimit -n $MAX_FD
95 | if [ $? -ne 0 ] ; then
96 | warn "Could not set maximum file descriptor limit: $MAX_FD"
97 | fi
98 | else
99 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
100 | fi
101 | fi
102 |
103 | # For Darwin, add options to specify how the application appears in the dock
104 | if $darwin; then
105 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
106 | fi
107 |
108 | # For Cygwin, switch paths to Windows format before running java
109 | if $cygwin ; then
110 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
111 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
112 | JAVACMD=`cygpath --unix "$JAVACMD"`
113 |
114 | # We build the pattern for arguments to be converted via cygpath
115 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
116 | SEP=""
117 | for dir in $ROOTDIRSRAW ; do
118 | ROOTDIRS="$ROOTDIRS$SEP$dir"
119 | SEP="|"
120 | done
121 | OURCYGPATTERN="(^($ROOTDIRS))"
122 | # Add a user-defined pattern to the cygpath arguments
123 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
124 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
125 | fi
126 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
127 | i=0
128 | for arg in "$@" ; do
129 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
130 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
131 |
132 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
133 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
134 | else
135 | eval `echo args$i`="\"$arg\""
136 | fi
137 | i=$((i+1))
138 | done
139 | case $i in
140 | (0) set -- ;;
141 | (1) set -- "$args0" ;;
142 | (2) set -- "$args0" "$args1" ;;
143 | (3) set -- "$args0" "$args1" "$args2" ;;
144 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
145 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
146 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
147 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
148 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
149 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
150 | esac
151 | fi
152 |
153 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
154 | function splitJvmOpts() {
155 | JVM_OPTS=("$@")
156 | }
157 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
158 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
159 |
160 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
161 |
--------------------------------------------------------------------------------
/app/src/main/java/net/winsion/www/indooratlasdemo/MainActivity.java:
--------------------------------------------------------------------------------
1 | package net.winsion.www.indooratlasdemo;
2 |
3 | import android.Manifest;
4 | import android.content.Context;
5 | import android.content.Intent;
6 | import android.os.Bundle;
7 | import android.support.v7.app.AppCompatActivity;
8 | import android.view.View;
9 | import android.view.inputmethod.InputMethodManager;
10 | import android.widget.Button;
11 | import android.widget.EditText;
12 | import android.widget.Toast;
13 |
14 | import com.anthonycr.grant.PermissionsManager;
15 | import com.anthonycr.grant.PermissionsResultAction;
16 | import com.orhanobut.logger.Logger;
17 |
18 | import java.util.ArrayList;
19 | import java.util.List;
20 | import java.util.Locale;
21 |
22 | public class MainActivity extends AppCompatActivity {
23 |
24 | private EditText mEdit;
25 | private List mClasses = new ArrayList<>();
26 | private Class mClass;
27 | boolean locationPermission;
28 | boolean storagePermission;
29 | ;
30 |
31 | @Override
32 | protected void onCreate(Bundle savedInstanceState) {
33 | super.onCreate(savedInstanceState);
34 | setContentView(R.layout.activity_main);
35 | mClasses.add(ImageViewActivity.class);
36 | mClasses.add(MapViewActivity.class);
37 | // mClass = mClasses.get(0); //默认使用ImageViewActivity
38 | mClass = mClasses.get(1); //默认使用MapViewActivity
39 | locationPermission = PermissionsManager.hasPermission(this, Manifest.permission.ACCESS_FINE_LOCATION);
40 | storagePermission = PermissionsManager.hasPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE);
41 |
42 | // findViewById(R.id.btn_imageView).setOnClickListener(new View.OnClickListener() {
43 | // @Override
44 | // public void onClick(View v) {
45 | // mClass = mClasses.get(0); //ImageViewActivity
46 | // Toast.makeText(getApplicationContext(), "已切换为ImageView", Toast.LENGTH_SHORT).show();
47 | // }
48 | // });
49 | // findViewById(R.id.btn_MapView).setOnClickListener(new View.OnClickListener() {
50 | // @Override
51 | // public void onClick(View v) {
52 | // mClass = mClasses.get(1); //MapViewActivity
53 | // Toast.makeText(getApplicationContext(), "已切换为MapView", Toast.LENGTH_SHORT).show();
54 | // }
55 | // });
56 | findViewById(R.id.btn_get_sensor_list).setOnClickListener(new View.OnClickListener() {
57 | @Override
58 | public void onClick(View v) {
59 | startActivity(new Intent(MainActivity.this, SensorListActivity.class));
60 | }
61 | });
62 |
63 | mEdit = (EditText) findViewById(R.id.et_input);
64 | findViewById(R.id.text1).setOnClickListener(new View.OnClickListener() {
65 | @Override
66 | public void onClick(View v) {
67 | if (checkPermission()) {
68 | startActivity(new Intent(MainActivity.this, mClass)
69 | .putExtra("floorPlanId", getString(R.string.indooratlas_floor_plan_id_26f)));
70 | }
71 | }
72 | });
73 | findViewById(R.id.text2).setOnClickListener(new View.OnClickListener() {
74 | @Override
75 | public void onClick(View v) {
76 | startActivity(new Intent(MainActivity.this, mClass)
77 | .putExtra("floorPlanId", getString(R.string.indooratlas_floor_plan_id_27f)));
78 | }
79 | });
80 | findViewById(R.id.text3).setOnClickListener(new View.OnClickListener() {
81 | @Override
82 | public void onClick(View v) {
83 | startActivity(new Intent(MainActivity.this, mClass)
84 | .putExtra("floorPlanId", getString(R.string.indooratlas_floor_plan_id_bjxz2f)));
85 | }
86 | });
87 | findViewById(R.id.confirm).setOnClickListener(new View.OnClickListener() {
88 | @Override
89 | public void onClick(View v) {
90 | if (mEdit.getText().length() < 36) {
91 | Toast.makeText(getApplicationContext(), "id格式有误", Toast.LENGTH_SHORT).show();
92 | } else {
93 | startActivity(new Intent(MainActivity.this, mClass)
94 | .putExtra("floorPlanId", mEdit.getText().toString().trim()));
95 | InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
96 | imm.toggleSoftInput(0, InputMethodManager.HIDE_NOT_ALWAYS);
97 | }
98 | }
99 | });
100 | }
101 |
102 | @Override
103 | protected void onResume() {
104 | super.onResume();
105 | }
106 |
107 | @Override
108 | protected void onPause() {
109 | super.onPause();
110 | }
111 |
112 | @Override
113 | protected void onDestroy() {
114 | super.onDestroy();
115 | }
116 |
117 | private boolean checkPermission() {
118 | Logger.i("Has " + Manifest.permission.ACCESS_FINE_LOCATION + " & "
119 | + Manifest.permission.WRITE_EXTERNAL_STORAGE + " permission: "
120 | + locationPermission + " & " + storagePermission);
121 | if (!locationPermission || !storagePermission) {
122 | PermissionsManager.getInstance().requestPermissionsIfNecessaryForResult(this,
123 | new String[]{
124 | Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS,
125 | Manifest.permission.ACCESS_COARSE_LOCATION,
126 | Manifest.permission.ACCESS_FINE_LOCATION,
127 | Manifest.permission.WRITE_EXTERNAL_STORAGE},
128 | new PermissionsResultAction() {
129 | @Override
130 | public void onGranted() {
131 | Logger.e("all permissions granted");
132 | }
133 |
134 | @Override
135 | public void onDenied(String permission) {
136 | String message = String.format(Locale.getDefault(), "Permission \\\"%1$s\\\" has been denied.", permission);
137 | Toast.makeText(MainActivity.this, message, Toast.LENGTH_SHORT).show();
138 | }
139 | });
140 | return false;
141 | } else {
142 | return true;
143 | }
144 | }
145 | }
146 |
--------------------------------------------------------------------------------
/.idea/codeStyleSettings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 | xmlns:android
80 | ^$
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 | xmlns:.*
90 | ^$
91 |
92 |
93 | BY_NAME
94 |
95 |
96 |
97 |
98 |
99 |
100 | .*:id
101 | http://schemas.android.com/apk/res/android
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 | .*:name
111 | http://schemas.android.com/apk/res/android
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 | name
121 | ^$
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
130 | style
131 | ^$
132 |
133 |
134 |
135 |
136 |
137 |
138 |
139 |
140 | .*
141 | ^$
142 |
143 |
144 | BY_NAME
145 |
146 |
147 |
148 |
149 |
150 |
151 | .*:layout_width
152 | http://schemas.android.com/apk/res/android
153 |
154 |
155 |
156 |
157 |
158 |
159 |
160 |
161 | .*:layout_height
162 | http://schemas.android.com/apk/res/android
163 |
164 |
165 |
166 |
167 |
168 |
169 |
170 |
171 | .*:layout_.*
172 | http://schemas.android.com/apk/res/android
173 |
174 |
175 | BY_NAME
176 |
177 |
178 |
179 |
180 |
181 |
182 | .*:width
183 | http://schemas.android.com/apk/res/android
184 |
185 |
186 | BY_NAME
187 |
188 |
189 |
190 |
191 |
192 |
193 | .*:height
194 | http://schemas.android.com/apk/res/android
195 |
196 |
197 | BY_NAME
198 |
199 |
200 |
201 |
202 |
203 |
204 | .*
205 | http://schemas.android.com/apk/res/android
206 |
207 |
208 | BY_NAME
209 |
210 |
211 |
212 |
213 |
214 |
215 | .*
216 | .*
217 |
218 |
219 | BY_NAME
220 |
221 |
222 |
223 |
224 |
225 |
226 |
227 |
228 |
229 |
--------------------------------------------------------------------------------
/MapViewLibrary/src/main/java/com/onlylemi/mapview/library/utils/MapMath.java:
--------------------------------------------------------------------------------
1 | package com.onlylemi.mapview.library.utils;
2 |
3 | import android.graphics.PointF;
4 | import android.util.Log;
5 |
6 | import com.onlylemi.mapview.library.utils.math.FloydAlgorithm;
7 | import com.onlylemi.mapview.library.utils.math.GeneticAlgorithm;
8 | import com.onlylemi.mapview.library.utils.math.TSPNearestNeighbour;
9 |
10 | import java.lang.annotation.Target;
11 | import java.util.ArrayList;
12 | import java.util.List;
13 |
14 | /**
15 | * MapMath
16 | *
17 | * @author onlylemi
18 | */
19 | public final class MapMath {
20 |
21 | /**
22 | * the distance between two points
23 | *
24 | * @param x1
25 | * @param y1
26 | * @param x2
27 | * @param y2
28 | * @return
29 | */
30 | public static float getDistanceBetweenTwoPoints(float x1, float y1,
31 | float x2, float y2) {
32 | return (float) Math.sqrt(Math.pow(x2 - x1, 2) + Math.pow(y2 - y1, 2));
33 | }
34 |
35 | /**
36 | * the distance between two points
37 | *
38 | * @param start
39 | * @param end
40 | * @return
41 | */
42 | public static float getDistanceBetweenTwoPoints(PointF start, PointF end) {
43 | return (float) Math.sqrt(Math.pow(end.x - start.x, 2)
44 | + Math.pow(end.y - start.y, 2));
45 | }
46 |
47 |
48 | /**
49 | * the shortest path between two points (FloydAlgorithm)
50 | *
51 | * @param begin
52 | * @param end
53 | * @param matrix adjacency matrix
54 | * @return
55 | */
56 | public static List getShortestPathBetweenTwoPoints(int begin,
57 | int end, float[][] matrix) {
58 | return FloydAlgorithm.getInstance().findCheapestPath(begin, end, matrix);
59 | }
60 |
61 | /**
62 | * the best path between some points (NearestNeighbour tsp)
63 | *
64 | * @param matrix adjacency matrix
65 | * @return
66 | */
67 | public static List getBestPathBetweenPointsByNearestNeighbour(float[][] matrix) {
68 | return TSPNearestNeighbour.getInstance().tsp(matrix);
69 | }
70 |
71 | /**
72 | * the best path between some points (GeneticAlgorithm tsp)
73 | *
74 | * @param matrix
75 | * @return
76 | */
77 | public static List getBestPathBetweenPointsByGeneticAlgorithm(float[][] matrix) {
78 | GeneticAlgorithm ga = GeneticAlgorithm.getInstance();
79 | ga.setAutoNextGeneration(true);
80 | ga.setMaxGeneration(200);
81 | int[] best = ga.tsp(matrix);
82 |
83 | List result = new ArrayList<>(best.length);
84 | for (int i = 0; i < best.length; i++) {
85 | result.add(best[i]);
86 | }
87 | return result;
88 | }
89 |
90 |
91 | /**
92 | * get the angle between two points and the horizontal plane
93 | *
94 | * @param start
95 | * @param end
96 | * @return
97 | */
98 | public static float getDegreeBetweenTwoPointsWithHorizontal(PointF start, PointF end) {
99 | float angle = 90.0f;
100 | if (start.x != end.x) {
101 | angle = (float) Math.toDegrees(Math.atan((end.y - start.y)
102 | / (end.x - start.x)));
103 | if (end.x < start.x && end.y >= start.y) {
104 | angle = angle + 180.0f;
105 | } else if (end.x < start.x && end.y > start.y) {
106 | angle = angle - 180.f;
107 | }
108 | } else {
109 | if (start.y < end.y) {
110 | angle = 90.0f;
111 | } else if (start.y > end.y) {
112 | angle = -90.0f;
113 | }
114 | }
115 | return angle;
116 | }
117 |
118 | /**
119 | * get the angle between two points and the vertical plane
120 | *
121 | * @param start
122 | * @param end
123 | * @return
124 | */
125 | public static float getDegreeBetweenTwoPointsWithVertical(PointF start, PointF end) {
126 | float angle = 90.0f;
127 | if (start.y != end.y) {
128 | angle = -(float) Math.toDegrees(Math.atan((end.x - start.x)
129 | / (end.y - start.y)));
130 | if (end.y > start.y && end.x >= start.x) {
131 | angle = angle + 180.0f;
132 | } else if (end.y > start.y && end.x > start.x) {
133 | angle = angle - 180.f;
134 | }
135 | } else {
136 | if (start.x < end.x) {
137 | angle = 90.0f;
138 | } else if (start.x > end.x) {
139 | angle = -90.0f;
140 | }
141 | }
142 | return angle;
143 | }
144 |
145 | /**
146 | * get degree between two points
147 | *
148 | * @param x1
149 | * @param y1
150 | * @param x2
151 | * @param y2
152 | * @return
153 | */
154 | public static float getDegreeBetweenTwoPoints(float x1, float y1, float x2, float y2) {
155 | double radians = Math.atan2(y1 - y2, x1 - x2);
156 | return (float) Math.toDegrees(radians);
157 | }
158 |
159 | /**
160 | * get degree between two points
161 | *
162 | * @param start
163 | * @param end
164 | * @return
165 | */
166 | public static float getDegreeBetweenTwoPoints(PointF start, PointF end) {
167 | return getDegreeBetweenTwoPoints(start.x, start.y, end.x, end.y);
168 | }
169 |
170 | /**
171 | * The coordinates of the midpoint between two points are obtained
172 | *
173 | * @param x1
174 | * @param y1
175 | * @param x2
176 | * @param y2
177 | * @return
178 | */
179 | public static PointF getMidPointBetweenTwoPoints(float x1, float y1, float x2, float y2) {
180 | return new PointF((x1 + x2) / 2, (y1 + y2) / 2);
181 | }
182 |
183 | /**
184 | * The coordinates of the midpoint between two points are obtained
185 | *
186 | * @param start
187 | * @param end
188 | * @return
189 | */
190 | public static PointF getMidPointBetweenTwoPoints(PointF start, PointF end) {
191 | return getMidPointBetweenTwoPoints(start.x, start.y, end.x, end.y);
192 | }
193 |
194 | /**
195 | * Get the coordinates of any point between two points
196 | *
197 | * @param start
198 | * @param end
199 | * @param value
200 | * @return
201 | */
202 | public static PointF getEveryPointBetweenTwoPoints(PointF start, PointF end, float value) {
203 | // y=kx+b
204 | float x, y;
205 | // with slope
206 | if (start.x != end.x) {
207 | float k = (end.y - start.y) / (end.x - start.x);
208 | float b = end.y - k * end.x;
209 |
210 | if (end.x > start.x) {
211 | x = Math.min(end.x, start.x) + (end.x - start.x) * value;
212 | } else {
213 | x = Math.max(end.x, start.x) + (end.x - start.x) * value;
214 | }
215 | y = k * x + b;
216 | } else { // no slope
217 | x = start.x;
218 | if (end.y > start.y) {
219 | y = Math.min(end.y, start.y) + (end.y - start.y) * value;
220 | } else {
221 | y = Math.max(end.y, start.y) + (end.y - start.y) * value;
222 | }
223 | }
224 | return new PointF(x, y);
225 | }
226 |
227 |
228 | /**
229 | * Get a shortest distance from point to line
230 | *
231 | * @param point
232 | * @param linePoint1 Determine the first point of a straight line
233 | * @param linePoint2 Determine the second point of a straight line
234 | * @return
235 | */
236 | public static float getDistanceFromPointToLine(PointF point, PointF linePoint1, PointF
237 | linePoint2) {
238 | // y = kx + b;
239 | // d = |kx-y+b| / √(k^2+1)
240 | float d;
241 | if (linePoint1.x != linePoint2.x) { // with slope
242 | float k = (linePoint2.y - linePoint1.y) / (linePoint2.x - linePoint1.x);
243 | float b = linePoint2.y - k * linePoint2.x;
244 | d = Math.abs(k * point.x - point.y + b) / (float) Math.sqrt(k * k + 1);
245 | } else { // no slope
246 | d = Math.abs(point.x - linePoint1.x);
247 | }
248 | return d;
249 | }
250 |
251 | /**
252 | * get intersection coordinates from a point to a line
253 | *
254 | * @param point
255 | * @param linePoint1
256 | * @param linePoint2
257 | * @return
258 | */
259 | public static PointF getIntersectionCoordinatesFromPointToLine(PointF point, PointF linePoint1, PointF
260 | linePoint2) {
261 | // y = kx + b;
262 | float x, y;
263 | if (linePoint1.x != linePoint2.x) { // with slope
264 | float k = (linePoint2.y - linePoint1.y) / (linePoint2.x - linePoint1.x);
265 | float b = linePoint2.y - k * linePoint2.x;
266 | // The equation of point
267 | if (k != 0) {
268 | float kV = -1 / k;
269 | float bV = point.y - kV * point.x;
270 | x = (b - bV) / (kV - k);
271 | y = kV * x + bV;
272 | } else {
273 | x = point.x;
274 | y = linePoint1.y;
275 | }
276 | } else { // no slope
277 | x = linePoint1.x;
278 | y = point.y;
279 | }
280 | return new PointF(x, y);
281 | }
282 |
283 | /**
284 | * is/not obtuse angle between a point and a line
285 | *
286 | * @param point
287 | * @param linePoint1
288 | * @param linePoint2
289 | * @return
290 | */
291 | public static boolean isObtuseAnglePointAndLine(PointF point, PointF linePoint1, PointF
292 | linePoint2) {
293 | // A*A + B*B < C*C
294 | float p_l1, p_l2, l1_l2;
295 | p_l1 = getDistanceBetweenTwoPoints(point, linePoint1);
296 | p_l2 = getDistanceBetweenTwoPoints(point, linePoint2);
297 | l1_l2 = getDistanceBetweenTwoPoints(linePoint1, linePoint2);
298 |
299 | return ((p_l1 * p_l1 + l1_l2 * l1_l2) < p_l2 * p_l2)
300 | || ((p_l2 * p_l2 + l1_l2 * l1_l2) < p_l1 * p_l1);
301 | }
302 |
303 | }
304 |
--------------------------------------------------------------------------------
/MapViewLibrary/src/main/java/com/onlylemi/mapview/library/utils/MapUtils.java:
--------------------------------------------------------------------------------
1 | package com.onlylemi.mapview.library.utils;
2 |
3 | import android.graphics.Bitmap;
4 | import android.graphics.Canvas;
5 | import android.graphics.Picture;
6 | import android.graphics.PointF;
7 | import android.graphics.RectF;
8 | import android.util.Log;
9 |
10 | import java.util.ArrayList;
11 | import java.util.List;
12 |
13 | /**
14 | * MapUtils
15 | *
16 | * @author onlylemi
17 | */
18 | public final class MapUtils {
19 |
20 | private static final String TAG = "MapUtils: ";
21 |
22 | private static final float INF = Float.MAX_VALUE;
23 | private static int nodesSize;
24 | private static int nodesContactSize;
25 |
26 | public static void init(int nodessize, int nodescontactsize) {
27 | nodesSize = nodessize;
28 | nodesContactSize = nodescontactsize;
29 | }
30 |
31 | /**
32 | * Get the distance between points
33 | *
34 | * @param nodes
35 | * @param list
36 | * @return
37 | */
38 | public static float getDistanceBetweenList(List nodes,
39 | List list) {
40 | float distance = 0;
41 | for (int i = 0; i < list.size() - 1; i++) {
42 | distance += MapMath.getDistanceBetweenTwoPoints(nodes.get(list.get(i)),
43 | nodes.get(list.get(i + 1)));
44 | }
45 | return distance;
46 | }
47 |
48 | /**
49 | * get degrees between two points(list) with horizontal plane
50 | *
51 | * @param routeList
52 | * @param nodes
53 | * @return
54 | */
55 | public static List getDegreeBetweenTwoPointsWithHorizontal(List routeList,
56 | List nodes) {
57 | List routeListDegrees = new ArrayList<>();
58 | for (int i = 0; i < routeList.size() - 1; i++) {
59 | routeListDegrees.add(MapMath.getDegreeBetweenTwoPointsWithHorizontal(nodes.get
60 | (routeList.get(i)),
61 | nodes.get(routeList.get(i + 1))));
62 | }
63 | return routeListDegrees;
64 | }
65 |
66 | /**
67 | * get degrees between two points(list) with vertical plane
68 | *
69 | * @param routeList
70 | * @param nodes
71 | * @return
72 | */
73 | public static List getDegreeBetweenTwoPointsWithVertical(List routeList,
74 | List nodes) {
75 | List routeListDegrees = new ArrayList<>();
76 | for (int i = 0; i < routeList.size() - 1; i++) {
77 | routeListDegrees.add(MapMath.getDegreeBetweenTwoPointsWithVertical(nodes.get
78 | (routeList.get(i)),
79 | nodes.get(routeList.get(i + 1))));
80 | }
81 | return routeListDegrees;
82 | }
83 |
84 | /**
85 | * get shortest path between two points
86 | *
87 | * @param start start point
88 | * @param end end point
89 | * @param nodes nodes list
90 | * @param nodesContact nodesContact list
91 | * @return
92 | */
93 | public static List getShortestPathBetweenTwoPoints(int start,
94 | int end, List nodes,
95 | List nodesContact) {
96 | float[][] matrix = getMatrixBetweenFloorPlanNodes(nodes, nodesContact);
97 |
98 | return MapMath.getShortestPathBetweenTwoPoints(start, end, matrix);
99 | }
100 |
101 | /**
102 | * get best path between points
103 | *
104 | * @param points
105 | * @param nodes
106 | * @param nodesContact
107 | * @return
108 | */
109 | public static List getBestPathBetweenPoints(int[] points, List nodes,
110 | List nodesContact) {
111 | // adjacency matrix
112 | float[][] matrix = new float[points.length][points.length];
113 | for (int i = 0; i < matrix.length; i++) {
114 | for (int j = i; j < matrix[i].length; j++) {
115 | if (i == j) {
116 | matrix[i][j] = INF;
117 | } else {
118 | matrix[i][j] = getDistanceBetweenList(
119 | nodes, getShortestPathBetweenTwoPoints(points[i],
120 | points[j], nodes, nodesContact));
121 | matrix[j][i] = matrix[i][j];
122 | }
123 | }
124 | }
125 |
126 | // TSP to get best path
127 | List routeList = new ArrayList<>();
128 | List result = MapMath.getBestPathBetweenPointsByGeneticAlgorithm(matrix);
129 | for (int i = 0; i < result.size() - 1; i++) {
130 | int size = routeList.size();
131 | routeList.addAll(getShortestPathBetweenTwoPoints(
132 | points[result.get(i)], points[result.get(i + 1)], nodes,
133 | nodesContact));
134 | if (i != 0) {
135 | routeList.remove(size);
136 | }
137 | }
138 | return routeList;
139 | }
140 |
141 |
142 | /**
143 | * get best path between points
144 | *
145 | * @param pointList
146 | * @param nodes
147 | * @param nodesContact
148 | * @return
149 | */
150 | public static List getBestPathBetweenPoints(List pointList,
151 | List nodes, List
152 | nodesContact) {
153 | if (nodesSize != nodes.size()) {
154 | int value = nodes.size() - nodesSize;
155 | for (int i = 0; i < value; i++) {
156 | nodes.remove(nodes.size() - 1);
157 | }
158 | value = nodesContact.size() - nodesContactSize;
159 | for (int i = 0; i < value; i++) {
160 | nodesContact.remove(nodesContact.size() - 1);
161 | }
162 | }
163 |
164 | //find the point on the nearest route
165 | int[] points = new int[pointList.size()];
166 | for (int i = 0; i < pointList.size(); i++) {
167 | addPointToList(pointList.get(i), nodes, nodesContact);
168 | points[i] = nodes.size() - 1;
169 | }
170 |
171 | return getBestPathBetweenPoints(points, nodes, nodesContact);
172 | }
173 |
174 | /**
175 | * get shortest distance between two points
176 | *
177 | * @param start
178 | * @param end
179 | * @param nodes
180 | * @param nodesContact
181 | * @return
182 | */
183 | public static float getShortestDistanceBetweenTwoPoints(int start, int end,
184 | List nodes, List
185 | nodesContact) {
186 | List list = getShortestPathBetweenTwoPoints(start, end, nodes,
187 | nodesContact);
188 | return getDistanceBetweenList(nodes, list);
189 | }
190 |
191 | /**
192 | * adjacency matrix with points
193 | *
194 | * @param nodes
195 | * @param nodesContact
196 | * @return
197 | */
198 | public static float[][] getMatrixBetweenFloorPlanNodes(List nodes, List
199 | nodesContact) {
200 | // set default is INF
201 | float[][] matrix = new float[nodes.size()][nodes.size()];
202 | for (int i = 0; i < matrix.length; i++) {
203 | for (int j = 0; j < matrix[i].length; j++) {
204 | matrix[i][j] = INF;
205 | }
206 | }
207 |
208 | // set value for matrix
209 | for (int i = 0; i < nodesContact.size(); i++) {
210 | matrix[(int) nodesContact.get(i).x][(int) nodesContact.get(i).y] = MapMath
211 | .getDistanceBetweenTwoPoints(nodes.get((int) nodesContact.get(i).x),
212 | nodes.get((int) nodesContact.get(i).y));
213 |
214 | matrix[(int) nodesContact.get(i).y][(int) nodesContact.get(i).x] = matrix[(int)
215 | nodesContact
216 | .get(i).x][(int) nodesContact.get(i).y];
217 | }
218 |
219 | return matrix;
220 | }
221 |
222 | /**
223 | * get shortest distance between two points
224 | *
225 | * @param start
226 | * @param end
227 | * @param nodes
228 | * @param nodesContact
229 | * @return
230 | */
231 | public static List getShortestDistanceBetweenTwoPoints(PointF start, PointF end,
232 | List nodes,
233 | List nodesContact) {
234 | if (nodesSize != nodes.size()) {
235 | int value = nodes.size() - nodesSize;
236 | for (int i = 0; i < value; i++) {
237 | nodes.remove(nodes.size() - 1);
238 | }
239 | value = nodesContact.size() - nodesContactSize;
240 | for (int i = 0; i < value; i++) {
241 | nodesContact.remove(nodesContact.size() - 1);
242 | }
243 | }
244 |
245 | addPointToList(start, nodes, nodesContact);
246 | addPointToList(end, nodes, nodesContact);
247 |
248 | return getShortestPathBetweenTwoPoints(nodes.size() - 2, nodes.size() - 1, nodes,
249 | nodesContact);
250 | }
251 |
252 | /**
253 | * add point to list
254 | *
255 | * @param point
256 | * @param nodes
257 | * @param nodesContact
258 | */
259 | private static void addPointToList(PointF point, List nodes, List nodesContact) {
260 | if (point != null) {
261 | PointF pV = null;
262 | int po1 = 0, po2 = 0;
263 | float min1 = INF;
264 | for (int i = 0; i < nodesContact.size() - 1; i++) {
265 | PointF p1 = nodes.get((int) nodesContact.get(i).x);
266 | PointF p2 = nodes.get((int) nodesContact.get(i).y);
267 | if (!MapMath.isObtuseAnglePointAndLine(point, p1, p2)) {
268 | float minDis = MapMath.getDistanceFromPointToLine(point, p1, p2);
269 | if (min1 > minDis) {
270 | pV = MapMath.getIntersectionCoordinatesFromPointToLine(point, p1, p2);
271 | min1 = minDis;
272 | po1 = (int) nodesContact.get(i).x;
273 | po2 = (int) nodesContact.get(i).y;
274 | }
275 | }
276 | }
277 | // get intersection
278 | nodes.add(pV);
279 | //Log.i(TAG, "node=" + (nodes.size() - 1) + ", po1=" + po1 + ", po2=" + po2);
280 | nodesContact.add(new PointF(po1, nodes.size() - 1));
281 | nodesContact.add(new PointF(po2, nodes.size() - 1));
282 | }
283 | }
284 |
285 | /**
286 | * get the shortest path from the position point to the target point in the map
287 | *
288 | * @param position
289 | * @param target
290 | * @param nodes
291 | * @param nodesContact
292 | * @return
293 | */
294 | public static List getShortestDistanceBetweenTwoPoints(PointF position, int target,
295 | List nodes,
296 | List nodesContact) {
297 | if (nodesSize != nodes.size()) {
298 | int value = nodes.size() - nodesSize;
299 | for (int i = 0; i < value; i++) {
300 | nodes.remove(nodes.size() - 1);
301 | }
302 | value = nodesContact.size() - nodesContactSize;
303 | for (int i = 0; i < value; i++) {
304 | nodesContact.remove(nodesContact.size() - 1);
305 | }
306 | }
307 |
308 | addPointToList(position, nodes, nodesContact);
309 |
310 | return getShortestPathBetweenTwoPoints(nodes.size() - 1, target, nodes, nodesContact);
311 | }
312 |
313 | /**
314 | * bitmap to picture
315 | *
316 | * @param bitmap
317 | * @return
318 | */
319 | public static Picture getPictureFromBitmap(Bitmap bitmap) {
320 | Picture picture = new Picture();
321 | Canvas canvas = picture.beginRecording(bitmap.getWidth(), bitmap.getHeight());
322 | canvas.drawBitmap(
323 | bitmap,
324 | null,
325 | new RectF(0f, 0f, (float) bitmap.getWidth(), (float) bitmap.getHeight()), null);
326 | picture.endRecording();
327 | return picture;
328 | }
329 | }
330 |
--------------------------------------------------------------------------------
/MapViewLibrary/src/main/java/com/onlylemi/mapview/library/utils/math/GeneticAlgorithm.java:
--------------------------------------------------------------------------------
1 | package com.onlylemi.mapview.library.utils.math;
2 |
3 | import java.util.Arrays;
4 | import java.util.Random;
5 |
6 | /**
7 | * GeneticAlgorithm
8 | *
9 | * @author: onlylemi
10 | */
11 | public class GeneticAlgorithm {
12 |
13 | private static final float DEFAULT_CROSSOVER_PROBABILITY = 0.9f; // 默认交叉概率
14 | private static final float DEFAULT_MUTATION_PROBABILITY = 0.01f; // 默认突变概率
15 | private static final int DEFAULT_POPULATION_SIZE = 30; // 默认种群数量
16 |
17 | private float crossoverProbability = DEFAULT_CROSSOVER_PROBABILITY; // 交叉概率
18 | private float mutationProbability = DEFAULT_MUTATION_PROBABILITY; // 突变概率
19 | private int populationSize = DEFAULT_POPULATION_SIZE; // 种群数量
20 |
21 | private int mutationTimes = 0; // 变异次数
22 | private int currentGeneration = 0; // 当前的一代
23 | private int maxGeneration = 1000; // 最大代数
24 |
25 | private int pointNum;
26 | private int[][] population; // 种群集
27 | private float[][] dist; // 点集间的邻接矩阵
28 |
29 | private int[] bestIndivial; // 最短的结果集
30 | private float bestDist; // 最短的距离
31 | private int currentBestPosition; // 当前最好个体的位置
32 | private float currentBestDist; // 当前最好个体的距离
33 |
34 | private float[] values; // 种群中每个个体的dist
35 | private float[] fitnessValues; // 适应度集
36 | private float[] roulette;
37 |
38 | private boolean isAutoNextGeneration = false;
39 |
40 | public static GeneticAlgorithm getInstance() {
41 | return GeneticAlgorithmHolder.instance;
42 | }
43 |
44 | private static class GeneticAlgorithmHolder {
45 | private static GeneticAlgorithm instance = new GeneticAlgorithm();
46 | }
47 |
48 | /**
49 | * 点集间的邻接矩阵
50 | *
51 | * @param matrix
52 | * @return
53 | */
54 | public int[] tsp(float[][] matrix) {
55 | this.dist = matrix;
56 | pointNum = matrix.length;
57 | init();
58 |
59 | if (isAutoNextGeneration) {
60 | int i = 0;
61 | while (i++ < maxGeneration) {
62 | nextGeneration();
63 | }
64 | }
65 | isAutoNextGeneration = false;
66 |
67 | return getBestIndivial();
68 | }
69 |
70 | /**
71 | * 初始化
72 | */
73 | private void init() {
74 | mutationTimes = 0;
75 | currentGeneration = 0;
76 | bestIndivial = null;
77 | bestDist = 0;
78 | currentBestPosition = 0;
79 | currentBestDist = 0;
80 |
81 | values = new float[populationSize];
82 | fitnessValues = new float[populationSize];
83 | roulette = new float[populationSize];
84 | population = new int[populationSize][pointNum];
85 |
86 | //initDist(points);
87 | // 父代
88 | for (int i = 0; i < populationSize; i++) {
89 | population[i] = randomIndivial(pointNum);
90 | }
91 | evaluateBestIndivial();
92 | }
93 |
94 | /**
95 | * 下一代
96 | */
97 | public int[] nextGeneration() {
98 | currentGeneration++;
99 |
100 | // 选择
101 | selection();
102 | // 交叉
103 | crossover();
104 | // 变异
105 | mutation();
106 | // 评价最好
107 | evaluateBestIndivial();
108 |
109 | return getBestIndivial();
110 | }
111 |
112 | /**
113 | * 选择
114 | */
115 | private void selection() {
116 | int[][] parents = new int[populationSize][pointNum];
117 |
118 | int initnum = 4;
119 | parents[0] = population[currentBestPosition]; // 当前种群中最好的个体
120 | parents[1] = exchangeMutate(bestIndivial.clone()); // 对最好的个体进行交换变异
121 | parents[2] = insertMutate(bestIndivial.clone()); // 对最好的个体进行插入变异
122 | parents[3] = bestIndivial.clone(); // 所有代中最好的个体
123 |
124 | setRoulette();
125 | for (int i = initnum; i < populationSize; i++) {
126 | parents[i] = population[wheelOut((int) Math.random())];
127 | }
128 | population = parents;
129 | }
130 |
131 | /**
132 | *
133 | */
134 | private void setRoulette() {
135 | //calculate all the fitness
136 | for (int i = 0; i < values.length; i++) {
137 | fitnessValues[i] = 1.0f / values[i]; // 适应度为路径长的导数
138 | }
139 |
140 | //set the roulette
141 | float sum = 0;
142 | for (int i = 0; i < fitnessValues.length; i++) {
143 | sum += fitnessValues[i];
144 | }
145 | for (int i = 0; i < roulette.length; i++) {
146 | roulette[i] = fitnessValues[i] / sum;
147 | }
148 | for (int i = 1; i < roulette.length; i++) {
149 | roulette[i] += roulette[i - 1];
150 | }
151 | }
152 |
153 | /**
154 | * 模拟转盘,进行子代选取
155 | *
156 | * @param ran
157 | * @return
158 | */
159 | private int wheelOut(int ran) {
160 | for (int i = 0; i < roulette.length; i++) {
161 | if (ran <= roulette[i]) {
162 | return i;
163 | }
164 | }
165 | return 0;
166 | }
167 |
168 |
169 | /**
170 | * 交换变异
171 | *
172 | * @param seq
173 | * @return
174 | */
175 | private int[] exchangeMutate(int[] seq) {
176 | mutationTimes++;
177 | int m, n;
178 | do {
179 | m = random(seq.length - 2);
180 | n = random(seq.length);
181 | } while (m >= n);
182 |
183 | int j = (n - m + 1) >> 1;
184 | for (int i = 0; i < j; i++) {
185 | int tmp = seq[m + i];
186 | seq[m + i] = seq[n - i];
187 | seq[n - i] = tmp;
188 | }
189 | return seq;
190 | }
191 |
192 | /**
193 | * 插入变异
194 | *
195 | * @param seq
196 | * @return
197 | */
198 | private int[] insertMutate(int[] seq) {
199 | mutationTimes++;
200 | int m, n;
201 | do {
202 | m = random(seq.length >> 1);
203 | n = random(seq.length);
204 | } while (m >= n);
205 |
206 | int[] s1 = Arrays.copyOfRange(seq, 0, m);
207 | int[] s2 = Arrays.copyOfRange(seq, m, n);
208 |
209 | for (int i = 0; i < m; i++) {
210 | seq[i + n - m] = s1[i];
211 | }
212 | for (int i = 0; i < n - m; i++) {
213 | seq[i] = s2[i];
214 | }
215 | return seq;
216 | }
217 |
218 | /**
219 | * 交叉
220 | */
221 | private void crossover() {
222 | int[] queue = new int[populationSize];
223 | int num = 0;
224 | for (int i = 0; i < populationSize; i++) {
225 | if (Math.random() < crossoverProbability) {
226 | queue[num] = i;
227 | num++;
228 | }
229 | }
230 | queue = Arrays.copyOfRange(queue, 0, num);
231 | queue = shuffle(queue);
232 | for (int i = 0; i < num - 1; i += 2) {
233 | doCrossover(queue[i], queue[i + 1]);
234 | }
235 | }
236 |
237 | private static final int PREVIOUS = 0;
238 | private static final int NEXT = 1;
239 |
240 | private void doCrossover(int x, int y) {
241 | population[x] = getChild(x, y, PREVIOUS);
242 | population[y] = getChild(x, y, NEXT);
243 | }
244 |
245 | /**
246 | * 根据父代求子代
247 | *
248 | * @param x
249 | * @param y
250 | * @param pos
251 | * @return
252 | */
253 | private int[] getChild(int x, int y, int pos) {
254 | int[] solution = new int[pointNum];
255 | int[] px = population[x].clone();
256 | int[] py = population[y].clone();
257 |
258 | int dx = 0, dy = 0;
259 | int c = px[random(px.length)];
260 | solution[0] = c;
261 |
262 | for (int i = 1; i < pointNum; i++) {
263 | int posX = indexOf(px, c);
264 | int posY = indexOf(py, c);
265 |
266 | if (pos == PREVIOUS) {
267 | dx = px[(posX + px.length - 1) % px.length];
268 | dy = py[(posY + py.length - 1) % py.length];
269 | } else if (pos == NEXT) {
270 | dx = px[(posX + px.length + 1) % px.length];
271 | dy = py[(posY + py.length + 1) % py.length];
272 | }
273 |
274 | for (int j = posX; j < px.length - 1; j++) {
275 | px[j] = px[j + 1];
276 | }
277 | px = Arrays.copyOfRange(px, 0, px.length - 1);
278 | for (int j = posY; j < py.length - 1; j++) {
279 | py[j] = py[j + 1];
280 | }
281 | py = Arrays.copyOfRange(py, 0, py.length - 1);
282 |
283 | c = dist[c][dx] < dist[c][dy] ? dx : dy;
284 |
285 | solution[i] = c;
286 | }
287 | return solution;
288 | }
289 |
290 | /**
291 | * 变异
292 | */
293 | private void mutation() {
294 | for (int i = 0; i < populationSize; i++) {
295 | if (Math.random() < mutationProbability) {
296 | if (Math.random() > 0.5) {
297 | population[i] = insertMutate(population[i]);
298 | } else {
299 | population[i] = exchangeMutate(population[i]);
300 | }
301 | i--;
302 | }
303 | }
304 | }
305 |
306 | /**
307 | * 评估最好的个体
308 | */
309 | private void evaluateBestIndivial() {
310 | for (int i = 0; i < population.length; i++) {
311 | values[i] = calculateIndivialDist(population[i]);
312 | }
313 | evaluateBestCurrentDist();
314 | if (bestDist == 0 || bestDist > currentBestDist) {
315 | bestDist = currentBestDist;
316 | bestIndivial = population[currentBestPosition].clone();
317 | }
318 | }
319 |
320 | /**
321 | * 计算个体的距离
322 | *
323 | * @return
324 | */
325 | private float calculateIndivialDist(int[] indivial) {
326 | float sum = dist[indivial[0]][indivial[indivial.length - 1]];
327 | for (int i = 1; i < indivial.length; i++) {
328 | sum += dist[indivial[i]][indivial[i - 1]];
329 | }
330 | return sum;
331 | }
332 |
333 | /**
334 | * 评估得到最短距离
335 | */
336 | public void evaluateBestCurrentDist() {
337 | currentBestDist = values[0];
338 | for (int i = 1; i < populationSize; i++) {
339 | if (values[i] < currentBestDist) {
340 | currentBestDist = values[i];
341 | currentBestPosition = i;
342 | }
343 | }
344 | }
345 |
346 |
347 | /**
348 | * 产生个体(乱序)
349 | *
350 | * @param n
351 | * @return
352 | */
353 | private int[] randomIndivial(int n) {
354 | int[] a = new int[n];
355 | for (int i = 0; i < n; i++) {
356 | a[i] = i;
357 | }
358 |
359 | return shuffle(a);
360 | }
361 |
362 | /**
363 | * 乱序处理
364 | *
365 | * @param a
366 | * @return
367 | */
368 | private int[] shuffle(int[] a) {
369 | for (int i = 0; i < a.length; i++) {
370 | int p = random(a.length);
371 | int tmp = a[i];
372 | a[i] = a[p];
373 | a[p] = tmp;
374 | }
375 | return a;
376 | }
377 |
378 | private static Random rd;
379 |
380 | private int random(int n) {
381 | Random ran = rd;
382 | if (ran == null) {
383 | ran = new Random();
384 | }
385 | return ran.nextInt(n);
386 | }
387 |
388 | private int[] concatAllArray(int[] first, int[]... rest) {
389 | int totalLength = first.length;
390 | for (int[] array : rest) {
391 | totalLength += array.length;
392 | }
393 | int[] result = Arrays.copyOf(first, totalLength);
394 | int offset = first.length;
395 | for (int[] array : rest) {
396 | System.arraycopy(array, 0, result, offset, array.length);
397 | offset += array.length;
398 | }
399 | return result;
400 | }
401 |
402 | private int indexOf(int[] a, int index) {
403 | for (int i = 0; i < a.length; i++) {
404 | if (a[i] == index) {
405 | return i;
406 | }
407 | }
408 | return 0;
409 | }
410 |
411 | public int[] getBestIndivial() {
412 | int[] best = new int[bestIndivial.length];
413 | int pos = indexOf(bestIndivial, 0);
414 |
415 | for (int i = 0; i < best.length; i++) {
416 | best[i] = bestIndivial[(i + pos) % bestIndivial.length];
417 | }
418 | return best;
419 | }
420 |
421 | public float getBestDist() {
422 | return bestDist;
423 | }
424 |
425 | public void setMaxGeneration(int maxGeneration) {
426 | this.maxGeneration = maxGeneration;
427 | }
428 |
429 | public void setAutoNextGeneration(boolean autoNextGeneration) {
430 | isAutoNextGeneration = autoNextGeneration;
431 | }
432 |
433 | public int getMutationTimes() {
434 | return mutationTimes;
435 | }
436 |
437 | public int getCurrentGeneration() {
438 | return currentGeneration;
439 | }
440 | }
441 |
--------------------------------------------------------------------------------
/app/src/main/java/net/winsion/www/indooratlasdemo/ImageViewActivity.java:
--------------------------------------------------------------------------------
1 | package net.winsion.www.indooratlasdemo;
2 |
3 | import android.annotation.SuppressLint;
4 | import android.app.AlertDialog;
5 | import android.app.DownloadManager;
6 | import android.app.ProgressDialog;
7 | import android.content.BroadcastReceiver;
8 | import android.content.Context;
9 | import android.content.Intent;
10 | import android.content.IntentFilter;
11 | import android.database.Cursor;
12 | import android.graphics.PointF;
13 | import android.hardware.Sensor;
14 | import android.hardware.SensorEvent;
15 | import android.hardware.SensorEventListener;
16 | import android.hardware.SensorManager;
17 | import android.net.Uri;
18 | import android.os.Build;
19 | import android.os.Bundle;
20 | import android.os.Environment;
21 | import android.os.Handler;
22 | import android.os.Looper;
23 | import android.os.Message;
24 | import android.support.v4.app.FragmentActivity;
25 | import android.text.TextUtils;
26 | import android.util.Log;
27 | import android.view.View;
28 | import android.widget.Button;
29 | import android.widget.TextView;
30 | import android.widget.Toast;
31 |
32 | import com.davemorrissey.labs.subscaleview.ImageSource;
33 | import com.indooratlas.android.sdk.IALocation;
34 | import com.indooratlas.android.sdk.IALocationListener;
35 | import com.indooratlas.android.sdk.IALocationManager;
36 | import com.indooratlas.android.sdk.IALocationRequest;
37 | import com.indooratlas.android.sdk.IARegion;
38 | import com.indooratlas.android.sdk.resources.IAFloorPlan;
39 | import com.indooratlas.android.sdk.resources.IALatLng;
40 | import com.indooratlas.android.sdk.resources.IALocationListenerSupport;
41 | import com.indooratlas.android.sdk.resources.IAResourceManager;
42 | import com.indooratlas.android.sdk.resources.IAResult;
43 | import com.indooratlas.android.sdk.resources.IAResultCallback;
44 | import com.indooratlas.android.sdk.resources.IATask;
45 | import com.orhanobut.logger.Logger;
46 |
47 | import net.winsion.www.indooratlasdemo.bean.Point;
48 | import net.winsion.www.indooratlasdemo.view.BlueDotView;
49 | import net.winsion.www.indooratlasdemo.utils.CommonMethord;
50 |
51 | import java.io.File;
52 | import java.util.ArrayList;
53 | import java.util.List;
54 |
55 | public class ImageViewActivity extends FragmentActivity implements SensorEventListener {
56 |
57 | private static final String TAG = "IndoorAtlasExample";
58 | private static final int EMPTY_MSG = 10000;
59 | private static final int MAP_FIXED = 1; //地图固定
60 | private static final int MAP_FOLLOW = 2; //地图跟随
61 | // blue dot radius in meters
62 | private static final float dotRadius = 0.3f;
63 | private IALocationManager mIALocationManager;
64 | private IAResourceManager mFloorPlanManager;
65 | private IATask mPendingAsyncResult;
66 | private IAFloorPlan mFloorPlan;
67 | private BlueDotView mImageView;
68 | private long mDownloadId;
69 | private DownloadManager mDownloadManager;
70 | private TextView mTextView;
71 | private ProgressDialog mProgressDialog;
72 | private SensorManager mSensorManager;
73 | private List mPointXYList = new ArrayList<>();
74 | private Button savePoints, showPoints, changeMode;
75 | private int mapMode;
76 | float mapDegree; // the rotate between reality map to northern
77 |
78 | private IALocationListener mLocationListener = new IALocationListenerSupport() {
79 | @SuppressLint("SetTextI18n")
80 | @Override
81 | public void onLocationChanged(IALocation location) {
82 | if (mFloorPlan != null) {
83 | Logger.i(mFloorPlan.toString());
84 | IALatLng latLng = new IALatLng(location.getLatitude(), location.getLongitude());
85 | PointF point = mFloorPlan.coordinateToPoint(latLng);
86 | mPointXYList.add(new Point(point.x, point.y, location.getLatitude(), location.getLongitude()));
87 |
88 | mTextView.setText("FloorName:" + mFloorPlan.getName() + '\n'
89 | + "latitude纬度:" + location.getLatitude() + '\n'
90 | + "longitude经度:" + location.getLongitude() + '\n'
91 | + "Accuracy精度:" + location.getAccuracy() + '\t'
92 | + "| Bearing方位:" + location.getBearing() + '\n'
93 | + "Region:" + location.getRegion().toString() + "\n"
94 | + "pointX:" + point.x + " | pointY:" + point.y + '\n'
95 | + "bitmapWidth&Height:" + mFloorPlan.getBitmapWidth() + "*" + mFloorPlan.getBitmapHeight());
96 |
97 | if (mImageView != null && mImageView.isReady()) {
98 | mImageView.setDotCenter(point);
99 | mImageView.setRangeIndicatorMeters(location.getAccuracy());
100 | mImageView.postInvalidate();
101 | if (mProgressDialog.isShowing()) {
102 | mHandler.sendEmptyMessage(EMPTY_MSG);
103 | }
104 | }
105 | }
106 | }
107 |
108 | @Override
109 | public void onStatusChanged(String provider, int status, Bundle extras) {
110 | super.onStatusChanged(provider, status, extras);
111 | }
112 | };
113 |
114 | private IARegion.Listener mRegionListener = new IARegion.Listener() {
115 |
116 | @Override
117 | public void onEnterRegion(IARegion region) {
118 | Logger.i(region.toString());
119 | //获取配置的floorId
120 | if (region.getType() == IARegion.TYPE_FLOOR_PLAN) {
121 | String id = region.getId();
122 | Log.i(TAG, "floorPlan changed to " + id);
123 | Toast.makeText(ImageViewActivity.this, id, Toast.LENGTH_SHORT).show();
124 | fetchFloorPlan(id);
125 | }
126 |
127 | }
128 |
129 | @Override
130 | public void onExitRegion(IARegion region) {
131 | // leaving a previously entered region
132 | }
133 |
134 | };
135 |
136 | private Handler mHandler = new Handler() {
137 | @Override
138 | public void handleMessage(Message msg) {
139 | super.handleMessage(msg);
140 | mProgressDialog.dismiss();
141 | }
142 | };
143 |
144 | @Override
145 | protected void onCreate(Bundle savedInstanceState) {
146 | super.onCreate(savedInstanceState);
147 | setContentView(R.layout.activity_image_view);
148 | // prevent the screen going to sleep while app is on foreground
149 | findViewById(android.R.id.content).setKeepScreenOn(true);
150 | initView();
151 |
152 | savePoints.setOnClickListener(new View.OnClickListener() {
153 | @Override
154 | public void onClick(View v) {
155 | //把坐标信息保存到根目录points.txt文件
156 | boolean isSave = CommonMethord.saveFile(CommonMethord.ListToStr(mPointXYList));
157 | if (isSave) {
158 | Toast.makeText(getApplicationContext(), "坐标点保存成功", Toast.LENGTH_SHORT).show();
159 | } else {
160 | Toast.makeText(getApplicationContext(), "坐标点保存失败", Toast.LENGTH_SHORT).show();
161 | }
162 | }
163 | });
164 |
165 | showPoints.setOnClickListener(new View.OnClickListener() {
166 | @Override
167 | public void onClick(View v) {
168 | String pointsData = CommonMethord.getFile(CommonMethord.getCurrentTime());
169 | new AlertDialog.Builder(ImageViewActivity.this)
170 | .setTitle("points data")
171 | .setMessage(pointsData)
172 | .create()
173 | .show();
174 | }
175 | });
176 |
177 | changeMode.setOnClickListener(new View.OnClickListener() {
178 | @Override
179 | public void onClick(View v) {
180 | if (mapMode == MAP_FIXED) {
181 | mapMode = MAP_FOLLOW;
182 | // mImageView.setDrawIndicator(false);
183 | changeMode.setText("切换为地图固定");
184 | } else {
185 | mapMode = MAP_FIXED;
186 | mImageView.setDrawIndicator(true);
187 | changeMode.setText("切换为地图跟随");
188 | }
189 | }
190 | });
191 |
192 | mDownloadManager = (DownloadManager) getSystemService(Context.DOWNLOAD_SERVICE);
193 | mIALocationManager = IALocationManager.create(this);
194 | mFloorPlanManager = IAResourceManager.create(this);
195 | //获取传感器服务
196 | mSensorManager = (SensorManager) getSystemService(SENSOR_SERVICE);
197 | /* optional setup of floor plan id
198 | if setLocation is not called, then location manager tries to find
199 | location automatically */
200 | setLocation(getIntent().getStringExtra("floorPlanId"));
201 | }
202 |
203 | private void initView() {
204 | mImageView = (BlueDotView) findViewById(R.id.imageView);
205 | mTextView = (TextView) findViewById(R.id.text_img);
206 | savePoints = (Button) findViewById(R.id.btn_save_point);
207 | showPoints = (Button) findViewById(R.id.btn_get_points);
208 | changeMode = (Button) findViewById(R.id.btn_change_display_mode);
209 | // changeMode.setVisibility(View.INVISIBLE);
210 | //默认地图固定
211 | mapMode = MAP_FIXED;
212 | mProgressDialog = new ProgressDialog(ImageViewActivity.this);
213 | mProgressDialog.setMessage("初始化地图数据...");
214 | mProgressDialog.setCanceledOnTouchOutside(false);
215 | mProgressDialog.show();
216 | }
217 |
218 | /**
219 | * 设置floorPlanId
220 | *
221 | * @param floorPlanId
222 | */
223 | private void setLocation(String floorPlanId) {
224 | if (!TextUtils.isEmpty(floorPlanId)) {
225 | final IALocation location = IALocation.from(IARegion.floorPlan(floorPlanId));
226 | mIALocationManager.setLocation(location);
227 | }
228 | }
229 |
230 | @Override
231 | public void onSensorChanged(SensorEvent event) {
232 | if (mImageView != null && mImageView.isReady()) {
233 | mapDegree = 85; // the rotate between reality map to northern
234 | float degree = 0;
235 | if (event.sensor.getType() == Sensor.TYPE_ORIENTATION) {
236 | degree = event.values[0];
237 | }
238 | showPoints.setText(degree + "");
239 |
240 | if (mapMode == MAP_FIXED) {
241 | //设置指示器旋转角度
242 | mImageView.setCompassIndicatorArrowRotateDegree(mapDegree + degree);
243 | }
244 | if (mapMode == MAP_FOLLOW) {
245 | //设置图片旋转
246 | mImageView.setRotation(degree + mapDegree);
247 | }
248 | mImageView.postInvalidate();
249 | }
250 | }
251 |
252 | @Override
253 | public void onAccuracyChanged(Sensor sensor, int accuracy) {
254 |
255 | }
256 |
257 | @Override
258 | protected void onResume() {
259 | super.onResume();
260 | // starts receiving location updates
261 | mIALocationManager.requestLocationUpdates(IALocationRequest.create(), mLocationListener);
262 | mIALocationManager.registerRegionListener(mRegionListener);
263 | //注册方向传感器
264 | mSensorManager.registerListener(this, mSensorManager.getDefaultSensor(Sensor.TYPE_ORIENTATION), SensorManager.SENSOR_DELAY_NORMAL);
265 | registerReceiver(onComplete, new IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE));
266 | }
267 |
268 | @Override
269 | protected void onPause() {
270 | super.onPause();
271 | mIALocationManager.removeLocationUpdates(mLocationListener);
272 | mIALocationManager.unregisterRegionListener(mRegionListener);
273 | mSensorManager.unregisterListener(this);
274 | unregisterReceiver(onComplete);
275 | Toast.makeText(getApplicationContext(), "定位中止", Toast.LENGTH_SHORT).show();
276 | }
277 |
278 | @Override
279 | protected void onDestroy() {
280 | super.onDestroy();
281 | mIALocationManager.destroy();
282 | }
283 |
284 | /* Broadcast receiver for floor plan image download */
285 | private BroadcastReceiver onComplete = new BroadcastReceiver() {
286 | @Override
287 | public void onReceive(Context context, Intent intent) {
288 | long id = intent.getLongExtra(DownloadManager.EXTRA_DOWNLOAD_ID, 0L);
289 | if (id != mDownloadId) {
290 | Log.w(TAG, "Ignore unrelated download");
291 | return;
292 | }
293 | Log.w(TAG, "Image download completed");
294 | Bundle extras = intent.getExtras();
295 | DownloadManager.Query q = new DownloadManager.Query();
296 | q.setFilterById(extras.getLong(DownloadManager.EXTRA_DOWNLOAD_ID));
297 | Cursor c = mDownloadManager.query(q);
298 |
299 | if (c.moveToFirst()) {
300 | int status = c.getInt(c.getColumnIndex(DownloadManager.COLUMN_STATUS));
301 | if (status == DownloadManager.STATUS_SUCCESSFUL) {
302 | // process download
303 | String filePath = c.getString(c.getColumnIndex(
304 | DownloadManager.COLUMN_LOCAL_FILENAME));
305 | showFloorPlanImage(filePath);
306 | }
307 | }
308 | c.close();
309 | }
310 | };
311 |
312 | /**
313 | * 加载地图
314 | *
315 | * @param filePath
316 | */
317 | private void showFloorPlanImage(String filePath) {
318 | Log.w(TAG, "showFloorPlanImage: " + filePath + "; MetersToPixels=" + mFloorPlan.getMetersToPixels());
319 | mImageView.setRadius(mFloorPlan.getMetersToPixels() * dotRadius);
320 | mImageView.setImage(ImageSource.uri(filePath));
321 | // mImageView.setCompassIndicatorArrowRotateDegree(60);
322 | // mImageView.setDotCenter(new PointF(500, 500));
323 | mProgressDialog.setMessage("请移动方位以完成初始化操作");
324 | }
325 |
326 | /**
327 | * Fetches floor plan data from IndoorAtlas server. Some room for cleaning up!!
328 | */
329 | private void fetchFloorPlan(String id) {
330 | cancelPendingNetworkCalls();
331 | final IATask asyncResult = mFloorPlanManager.fetchFloorPlanWithId(id);
332 | mPendingAsyncResult = asyncResult;
333 | if (mPendingAsyncResult != null) {
334 | mPendingAsyncResult.setCallback(new IAResultCallback() {
335 | @Override
336 | public void onResult(IAResult result) {
337 | Log.e(TAG, "fetch floor plan result:" + result);
338 | if (result.isSuccess() && result.getResult() != null) {
339 | mFloorPlan = result.getResult();
340 | String fileName = mFloorPlan.getId() + ".jpg";
341 | String filePath = Environment.getExternalStorageDirectory() + "/"
342 | + Environment.DIRECTORY_DOWNLOADS + "/" + fileName;
343 | File file = new File(filePath);
344 | if (!file.exists()) {
345 | DownloadManager.Request request =
346 | new DownloadManager.Request(Uri.parse(mFloorPlan.getUrl()));
347 | request.setDescription("IndoorAtlas floor plan");
348 | request.setTitle("Floor plan");
349 | // requires android 3.2 or later to compile
350 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
351 | request.allowScanningByMediaScanner();
352 | request.setNotificationVisibility(DownloadManager.
353 | Request.VISIBILITY_HIDDEN);
354 | }
355 | request.setDestinationInExternalPublicDir(Environment.
356 | DIRECTORY_DOWNLOADS, fileName);
357 |
358 | mDownloadId = mDownloadManager.enqueue(request);
359 | } else {
360 | showFloorPlanImage(filePath);
361 | }
362 | } else {
363 | // do something with error
364 | if (!asyncResult.isCancelled()) {
365 | Toast.makeText(ImageViewActivity.this,
366 | (result.getError() != null
367 | ? "error loading floor plan: " + result.getError()
368 | : "access to floor plan denied"),
369 | Toast.LENGTH_LONG).show();
370 | }
371 | }
372 | }
373 | }, Looper.getMainLooper()); // deliver callbacks in main thread
374 | }
375 | }
376 |
377 | private void cancelPendingNetworkCalls() {
378 | if (mPendingAsyncResult != null && !mPendingAsyncResult.isCancelled()) {
379 | mPendingAsyncResult.cancel();
380 | }
381 | }
382 |
383 | }
384 |
385 |
--------------------------------------------------------------------------------
/MapViewLibrary/src/main/java/com/onlylemi/mapview/library/MapView.java:
--------------------------------------------------------------------------------
1 | package com.onlylemi.mapview.library;
2 |
3 | import android.content.Context;
4 | import android.graphics.Bitmap;
5 | import android.graphics.Canvas;
6 | import android.graphics.Matrix;
7 | import android.graphics.Picture;
8 | import android.graphics.PointF;
9 | import android.graphics.RectF;
10 | import android.util.AttributeSet;
11 | import android.util.Log;
12 | import android.view.MotionEvent;
13 | import android.view.SurfaceHolder;
14 | import android.view.SurfaceView;
15 |
16 | import com.onlylemi.mapview.library.layer.MapBaseLayer;
17 | import com.onlylemi.mapview.library.layer.MapLayer;
18 | import com.onlylemi.mapview.library.utils.MapMath;
19 | import com.onlylemi.mapview.library.utils.MapUtils;
20 | import com.onlylemi.mapview.library.utils.ThreadHelper;
21 |
22 | import java.util.ArrayList;
23 | import java.util.List;
24 | import java.util.concurrent.ThreadPoolExecutor;
25 |
26 | /**
27 | * MapView
28 | */
29 | public class MapView extends SurfaceView implements SurfaceHolder.Callback {
30 |
31 | private static final String TAG = "MapView";
32 |
33 | private SurfaceHolder holder;
34 | private MapViewListener mapViewListener = null;
35 | private boolean isMapLoadFinish = false;
36 | private List layers; // all layers
37 | private MapLayer mapLayer;
38 |
39 | private Canvas canvas;
40 |
41 | private float minZoom = 0.5f;
42 | private float maxZoom = 4.0f;
43 |
44 | private PointF startTouch = new PointF();
45 | private PointF mid = new PointF();
46 |
47 | private Matrix saveMatrix = new Matrix();
48 | private Matrix currentMatrix = new Matrix();
49 | private float currentZoom = 1.0f;
50 | private float saveZoom = 0f;
51 | private float currentRotateDegrees = 0.0f;
52 | private float saveRotateDegrees = 0.0f;
53 |
54 | private static final int TOUCH_STATE_NO = 0; // no touch
55 | private static final int TOUCH_STATE_SCROLL = 1; // scroll(one point)
56 | private static final int TOUCH_STATE_SCALE = 2; // scale(two points)
57 | private static final int TOUCH_STATE_ROTATE = 3; // rotate(two points)
58 | private static final int TOUCH_STATE_TWO_POINTED = 4; // two points touch
59 | private int currentTouchState = MapView.TOUCH_STATE_NO; // default touch state
60 |
61 | private float oldDist = 0, oldDegree = 0;
62 | private boolean isScaleAndRotateTogether = false;
63 | private boolean running;
64 |
65 | public MapView(Context context) {
66 | this(context, null);
67 | }
68 |
69 | public MapView(Context context, AttributeSet attrs) {
70 | this(context, attrs, 0);
71 | }
72 |
73 | public MapView(Context context, AttributeSet attrs, int defStyleAttr) {
74 | super(context, attrs, defStyleAttr);
75 | initMapView();
76 | }
77 |
78 |
79 | /**
80 | * init mapview
81 | */
82 | private void initMapView() {
83 | holder = this.getHolder();
84 | holder.addCallback(this);
85 |
86 | layers = new ArrayList() {
87 | @Override
88 | public boolean add(MapBaseLayer layer) {
89 | if (layers.size() != 0) {
90 | if (layer.level >= this.get(this.size() - 1).level) {
91 | super.add(layer);
92 | } else {
93 | for (int i = 0; i < layers.size(); i++) {
94 | if (layer.level < this.get(i).level) {
95 | super.add(i, layer);
96 | break;
97 | }
98 | }
99 | }
100 | } else {
101 | super.add(layer);
102 | }
103 | return true;
104 | }
105 | };
106 | }
107 |
108 | @Override
109 | public void surfaceCreated(SurfaceHolder holder) {
110 | refresh();
111 | }
112 |
113 | @Override
114 | public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
115 |
116 | }
117 |
118 | @Override
119 | public void surfaceDestroyed(SurfaceHolder holder) {
120 | Log.e(TAG,"surfaceDestroyed>>>>>>>>>>>");
121 | // running = false;
122 | }
123 |
124 | /**
125 | * reload mapview
126 | */
127 | public void refresh() {
128 | ThreadHelper.getThreadPool().execute(new Runnable() {
129 | @Override
130 | public void run() {
131 | while (running) {
132 | //判断Surface是否已经准备好
133 | if (holder != null) {
134 | canvas = holder.lockCanvas();
135 | if (canvas != null) {
136 | canvas.drawColor(-1); //画个白底
137 | if (isMapLoadFinish) {
138 | for (MapBaseLayer layer : layers) {
139 | if (layer.isVisible) {
140 | layer.draw(canvas, currentMatrix, currentZoom, currentRotateDegrees);
141 | }
142 | }
143 | }
144 | holder.unlockCanvasAndPost(canvas);
145 | }
146 | }
147 | }
148 | }
149 | });
150 |
151 | }
152 |
153 | /**
154 | * load map
155 | *
156 | * @param bitmap
157 | */
158 | public void loadMap(final Bitmap bitmap) {
159 | // loadMap(MapUtils.getPictureFromBitmap(bitmap));
160 | isMapLoadFinish = false;
161 | new Thread(new Runnable() {
162 | @Override
163 | public void run() {
164 | if (bitmap != null) {
165 | if (mapLayer == null) {
166 | mapLayer = new MapLayer(MapView.this);
167 | // add map image layer
168 | layers.add(mapLayer);
169 | }
170 | mapLayer.setImage(bitmap);
171 | if (mapViewListener != null) {
172 | // load map success, and callback
173 | mapViewListener.onMapLoadSuccess();
174 | } else {
175 | Log.e(TAG, "mapViewListener is null");
176 | }
177 | isMapLoadFinish = true;
178 | refresh();
179 | } else {
180 | if (mapViewListener != null) {
181 | mapViewListener.onMapLoadFail();
182 | }
183 | }
184 | }
185 | }).start();
186 |
187 | }
188 |
189 | // /**
190 | // * load map image
191 | // *
192 | // * @param picture
193 | // */
194 | // /*public void loadMap(final Picture picture) {
195 | // isMapLoadFinish = false;
196 | // new Thread(new Runnable() {
197 | // @Override
198 | // public void run() {
199 | // if (picture != null) {
200 | // if (mapLayer == null) {
201 | // mapLayer = new MapLayer(MapView.this);
202 | // // add map image layer
203 | // layers.add(mapLayer);
204 | // }
205 | // mapLayer.setImage(picture);
206 | // if (mapViewListener != null) {
207 | // // load map success, and callback
208 | // mapViewListener.onMapLoadSuccess();
209 | // } else {
210 | // Log.e(TAG, "mapViewListener is null");
211 | // }
212 | // isMapLoadFinish = true;
213 | // refresh();
214 | // } else {
215 | // if (mapViewListener != null) {
216 | // mapViewListener.onMapLoadFail();
217 | // }
218 | // }
219 | // }
220 | // }).start();
221 | //
222 | // }*/
223 | @Override
224 | public boolean onTouchEvent(MotionEvent event) {
225 | if (!isMapLoadFinish) {
226 | return false;
227 | }
228 |
229 | float newDist;
230 | float newDegree;
231 |
232 | switch (event.getAction() & MotionEvent.ACTION_MASK) {
233 | case MotionEvent.ACTION_DOWN:
234 | saveMatrix.set(currentMatrix);
235 | startTouch.set(event.getX(), event.getY());
236 | currentTouchState = MapView.TOUCH_STATE_SCROLL;
237 | break;
238 | case MotionEvent.ACTION_UP:
239 | if (withFloorPlan(event.getX(), event.getY())) {
240 | // Log.i(TAG, event.getX() + " " + event.getY());
241 | // layers on touch
242 | for (MapBaseLayer layer : layers) {
243 | layer.onTouch(event);
244 | }
245 | }
246 | currentTouchState = MapView.TOUCH_STATE_NO;
247 | break;
248 | case MotionEvent.ACTION_POINTER_DOWN:
249 | if (event.getPointerCount() == 2) {
250 | saveMatrix.set(currentMatrix);
251 | saveZoom = currentZoom;
252 | saveRotateDegrees = currentRotateDegrees;
253 | startTouch.set(event.getX(0), event.getY(0));
254 | currentTouchState = MapView.TOUCH_STATE_TWO_POINTED;
255 |
256 | mid = midPoint(event);
257 | oldDist = distance(event, mid);
258 | oldDegree = rotation(event, mid);
259 | }
260 | break;
261 | case MotionEvent.ACTION_POINTER_UP:
262 | currentTouchState = MapView.TOUCH_STATE_NO;
263 | break;
264 | case MotionEvent.ACTION_MOVE:
265 | switch (currentTouchState) {
266 | case MapView.TOUCH_STATE_SCROLL:
267 | currentMatrix.set(saveMatrix);
268 | currentMatrix.postTranslate(event.getX() - startTouch.x, event.getY() - startTouch.y);
269 | refresh();
270 | break;
271 | case MapView.TOUCH_STATE_TWO_POINTED:
272 | if (!isScaleAndRotateTogether) {
273 | float x = oldDist;
274 | float y = MapMath.getDistanceBetweenTwoPoints(event.getX(0),
275 | event.getY(0), startTouch.x, startTouch.y);
276 | float z = distance(event, mid);
277 | float cos = (x * x + y * y - z * z) / (2 * x * y);
278 | float degree = (float) Math.toDegrees(Math.acos(cos));
279 |
280 | if (degree < 120 && degree > 45) {
281 | oldDegree = rotation(event, mid);
282 | currentTouchState = MapView.TOUCH_STATE_ROTATE;
283 | } else {
284 | oldDist = distance(event, mid);
285 | currentTouchState = MapView.TOUCH_STATE_SCALE;
286 | }
287 | } else {
288 | currentMatrix.set(saveMatrix);
289 | newDist = distance(event, mid);
290 | newDegree = rotation(event, mid);
291 |
292 | float rotate = newDegree - oldDegree;
293 | float scale = newDist / oldDist;
294 | if (scale * saveZoom < minZoom) {
295 | scale = minZoom / saveZoom;
296 | } else if (scale * saveZoom > maxZoom) {
297 | scale = maxZoom / saveZoom;
298 | }
299 | currentZoom = scale * saveZoom;
300 | currentRotateDegrees = (newDegree - oldDegree + currentRotateDegrees)
301 | % 360;
302 | currentMatrix.postScale(scale, scale, mid.x, mid.y);
303 | currentMatrix.postRotate(rotate, mid.x, mid.y);
304 | refresh();
305 | }
306 | break;
307 | case MapView.TOUCH_STATE_SCALE:
308 | currentMatrix.set(saveMatrix);
309 | newDist = distance(event, mid);
310 | float scale = newDist / oldDist;
311 | if (scale * saveZoom < minZoom) {
312 | scale = minZoom / saveZoom;
313 | } else if (scale * saveZoom > maxZoom) {
314 | scale = maxZoom / saveZoom;
315 | }
316 | currentZoom = scale * saveZoom;
317 | currentMatrix.postScale(scale, scale, mid.x, mid.y);
318 | refresh();
319 | break;
320 | case MapView.TOUCH_STATE_ROTATE:
321 | currentMatrix.set(saveMatrix);
322 | newDegree = rotation(event, mid);
323 | float rotate = newDegree - oldDegree;
324 | currentRotateDegrees = (rotate + saveRotateDegrees) % 360;
325 | currentRotateDegrees = currentRotateDegrees > 0 ? currentRotateDegrees :
326 | currentRotateDegrees + 360;
327 | currentMatrix.postRotate(rotate, mid.x, mid.y);
328 | refresh();
329 | // Log.i(TAG, "rotate:" + currentRotateDegrees);
330 | break;
331 | default:
332 | break;
333 | }
334 | break;
335 | default:
336 | break;
337 | }
338 | return true;
339 | }
340 |
341 | /**
342 | * set mapview listener
343 | *
344 | * @param mapViewListener
345 | */
346 | public void setMapViewListener(MapViewListener mapViewListener) {
347 | this.mapViewListener = mapViewListener;
348 | }
349 |
350 | /**
351 | * convert coordinate of map to coordinate of screen
352 | *
353 | * @param x
354 | * @param y
355 | * @return
356 | */
357 | public float[] convertMapXYToScreenXY(float x, float y) {
358 | Matrix invertMatrix = new Matrix();
359 | float value[] = {x, y};
360 | currentMatrix.invert(invertMatrix);
361 | invertMatrix.mapPoints(value);
362 | return value;
363 | }
364 |
365 | /**
366 | * map is/not load finish
367 | *
368 | * @return
369 | */
370 | public boolean isMapLoadFinish() {
371 | return isMapLoadFinish;
372 | }
373 |
374 | /**
375 | * add layer
376 | *
377 | * @param layer
378 | */
379 | public void addLayer(MapBaseLayer layer) {
380 | if (layer != null) {
381 | layers.add(layer);
382 | }
383 | }
384 |
385 | /**
386 | * get all layers
387 | *
388 | * @return
389 | */
390 | public List getLayers() {
391 | return layers;
392 | }
393 |
394 | public void translate(float x, float y) {
395 | currentMatrix.postTranslate(x, y);
396 | }
397 |
398 | /**
399 | * set point to map center
400 | *
401 | * @param x
402 | * @param y
403 | */
404 | public void mapCenterWithPoint(float x, float y) {
405 | float[] goal = {x, y};
406 | currentMatrix.mapPoints(goal);
407 |
408 | float deltaX = getWidth() / 2 - goal[0];
409 | float deltaY = getHeight() / 2 - goal[1];
410 | currentMatrix.postTranslate(deltaX, deltaY);
411 | }
412 |
413 | public float getCurrentRotateDegrees() {
414 | return currentRotateDegrees;
415 | }
416 |
417 | /**
418 | * set rotate degrees
419 | *
420 | * @param degrees
421 | */
422 | public void setCurrentRotateDegrees(float degrees) {
423 | mapCenterWithPoint(getMapWidth() / 2, getMapHeight() / 2);
424 | setCurrentRotateDegrees(degrees, getWidth() / 2, getHeight() / 2);
425 | }
426 |
427 | /**
428 | * set rotate degrees
429 | *
430 | * @param degrees
431 | * @param x
432 | * @param y
433 | */
434 | public void setCurrentRotateDegrees(float degrees, float x, float y) {
435 | currentMatrix.postRotate(degrees - currentRotateDegrees, x, y);
436 |
437 | currentRotateDegrees = degrees % 360;
438 | currentRotateDegrees = currentRotateDegrees > 0 ? currentRotateDegrees : currentRotateDegrees + 360;
439 | }
440 |
441 | public float getCurrentZoom() {
442 | return currentZoom;
443 | }
444 |
445 | public boolean isScaleAndRotateTogether() {
446 | return isScaleAndRotateTogether;
447 | }
448 |
449 | /**
450 | * setting scale&rotate is/not together on touch
451 | *
452 | * @param scaleAndRotateTogether
453 | */
454 | public void setScaleAndRotateTogether(boolean scaleAndRotateTogether) {
455 | isScaleAndRotateTogether = scaleAndRotateTogether;
456 | }
457 |
458 | public void setMaxZoom(float maxZoom) {
459 | this.maxZoom = maxZoom;
460 | }
461 |
462 | public void setMinZoom(float minZoom) {
463 | this.minZoom = minZoom;
464 | }
465 |
466 | public void setCurrentZoom(float zoom) {
467 | setCurrentZoom(zoom, getWidth() / 2, getHeight() / 2);
468 | }
469 |
470 | public void setCurrentZoom(float zoom, float x, float y) {
471 | currentMatrix.postScale(zoom / this.currentZoom, zoom / this.currentZoom, x, y);
472 | this.currentZoom = zoom;
473 | }
474 |
475 | private PointF midPoint(MotionEvent event) {
476 | return MapMath.getMidPointBetweenTwoPoints(event.getX(0), event.getY(0)
477 | , event.getX(1), event.getY(1));
478 | }
479 |
480 | private float distance(MotionEvent event, PointF mid) {
481 | return MapMath.getDistanceBetweenTwoPoints(event.getX(0), event.getY(0)
482 | , mid.x, mid.y);
483 | }
484 |
485 | private float rotation(MotionEvent event, PointF mid) {
486 | return MapMath.getDegreeBetweenTwoPoints(event.getX(0), event.getY(0)
487 | , mid.x, mid.y);
488 | }
489 |
490 | /**
491 | * point is/not in floor plan
492 | *
493 | * @param x
494 | * @param y
495 | * @return
496 | */
497 | public boolean withFloorPlan(float x, float y) {
498 | float[] goal = convertMapXYToScreenXY(x, y);
499 | return goal[0] > 0 && goal[0] < mapLayer.getImage().getWidth() && goal[1] > 0
500 | && goal[1] < mapLayer.getImage().getHeight();
501 | }
502 |
503 | public float getMapWidth() {
504 | return mapLayer.getImage().getWidth();
505 | }
506 |
507 | public float getMapHeight() {
508 | return mapLayer.getImage().getHeight();
509 | }
510 |
511 | public void setRunning(boolean running) {
512 | this.running = running;
513 | }
514 | }
515 |
--------------------------------------------------------------------------------
/app/src/main/java/net/winsion/www/indooratlasdemo/MapViewActivity.java:
--------------------------------------------------------------------------------
1 | package net.winsion.www.indooratlasdemo;
2 |
3 | import android.Manifest;
4 | import android.annotation.SuppressLint;
5 | import android.app.DownloadManager;
6 | import android.app.ProgressDialog;
7 | import android.content.BroadcastReceiver;
8 | import android.content.Context;
9 | import android.content.DialogInterface;
10 | import android.content.Intent;
11 | import android.content.IntentFilter;
12 | import android.database.Cursor;
13 | import android.graphics.Bitmap;
14 | import android.graphics.BitmapFactory;
15 | import android.graphics.PointF;
16 | import android.hardware.Sensor;
17 | import android.hardware.SensorEvent;
18 | import android.hardware.SensorEventListener;
19 | import android.hardware.SensorManager;
20 | import android.net.Uri;
21 | import android.os.Build;
22 | import android.os.Bundle;
23 | import android.os.Environment;
24 | import android.os.Looper;
25 | import android.support.annotation.NonNull;
26 | import android.support.v7.app.AlertDialog;
27 | import android.support.v7.app.AppCompatActivity;
28 | import android.text.TextUtils;
29 | import android.util.Log;
30 | import android.view.Gravity;
31 | import android.view.View;
32 | import android.widget.Button;
33 | import android.widget.TextView;
34 | import android.widget.Toast;
35 |
36 | import com.anthonycr.grant.PermissionsManager;
37 | import com.anthonycr.grant.PermissionsResultAction;
38 | import com.indooratlas.android.sdk.IALocation;
39 | import com.indooratlas.android.sdk.IALocationListener;
40 | import com.indooratlas.android.sdk.IALocationManager;
41 | import com.indooratlas.android.sdk.IALocationRequest;
42 | import com.indooratlas.android.sdk.IARegion;
43 | import com.indooratlas.android.sdk.resources.IAFloorPlan;
44 | import com.indooratlas.android.sdk.resources.IALatLng;
45 | import com.indooratlas.android.sdk.resources.IALocationListenerSupport;
46 | import com.indooratlas.android.sdk.resources.IAResourceManager;
47 | import com.indooratlas.android.sdk.resources.IAResult;
48 | import com.indooratlas.android.sdk.resources.IAResultCallback;
49 | import com.indooratlas.android.sdk.resources.IATask;
50 | import com.onlylemi.mapview.library.MapView;
51 | import com.onlylemi.mapview.library.MapViewListener;
52 | import com.onlylemi.mapview.library.layer.LocationLayer;
53 | import com.onlylemi.mapview.library.layer.MarkLayer;
54 | import com.onlylemi.mapview.library.layer.RouteLayer;
55 | import com.onlylemi.mapview.library.utils.MapUtils;
56 | import com.orhanobut.logger.Logger;
57 |
58 | import net.winsion.www.indooratlasdemo.bean.Point;
59 | import net.winsion.www.indooratlasdemo.utils.CommonMethord;
60 |
61 | import java.io.File;
62 | import java.util.ArrayList;
63 | import java.util.List;
64 | import java.util.Locale;
65 |
66 | /**
67 | * Created by dys on 2016/5/8 0008.
68 | * 室内定位
69 | */
70 | public class MapViewActivity extends AppCompatActivity implements View.OnClickListener, SensorEventListener {
71 |
72 | private static final String TAG = "MapViewActivity";
73 | private static final int MAP_FIXED = 1; //地图固定
74 | private static final int MAP_FOLLOW = 2; //地图跟随
75 | private MapView mapView;
76 | private LocationLayer mLocationLayer;
77 | private MarkLayer mMarkLayer;
78 | private RouteLayer mRouteLayer;
79 | private IALocationManager mIALocationManager;
80 | private IAResourceManager mFloorPlanManager;
81 | private IATask mPendingAsyncResult;
82 | private IAFloorPlan mFloorPlan;
83 | private long mDownloadId;
84 | private DownloadManager mDownloadManager;
85 | private TextView mTextView;
86 | private ProgressDialog mProgressDialog;
87 | private SensorManager mSensorManager;
88 | private List mPointXYList = new ArrayList<>();
89 | private Button savePoints, showPoints, changeMode;
90 | private int mapMode;
91 | private float mapDegree = 80; // the rotate between reality map to northern
92 | private float degree = 0;
93 | private PointF mPointF, centerPoint;
94 | private boolean beginDirection = false;
95 | private Bitmap mFloorMap = null; //当前楼层地图
96 | private boolean useIndoor = false;
97 | String filePath;
98 |
99 | private IALocationListener mLocationListener = new IALocationListenerSupport() {
100 | @SuppressLint("SetTextI18n")
101 | @Override
102 | public void onLocationChanged(IALocation location) {
103 | if (mFloorPlan != null) {
104 | IALatLng latLng = new IALatLng(location.getLatitude(), location.getLongitude());
105 | mPointF = mFloorPlan.coordinateToPoint(latLng);
106 | // mPointXYList.add(new Point(mPointF.x, mPointF.y, location.getLatitude(), location.getLongitude()));
107 | // mTextView.setText("FloorName:" + mFloorPlan.getName() + '\n'
108 | // + "latitude纬度:" + location.getLatitude() + '\n'
109 | // + "longitude经度:" + location.getLongitude() + '\n'
110 | // + "Accuracy精度:" + location.getAccuracy() + '\t'
111 | // + "| Bearing方位:" + location.getBearing() + '\n'
112 | // + "Region:" + location.getRegion().toString()
113 | // + "pointX:" + mPointF.x + " | pointY:" + mPointF.y + '\n'
114 | // + "bitmapWidth&Height:" + mFloorPlan.getBitmapWidth() + "*" + mFloorPlan.getBitmapHeight());
115 |
116 | if (mLocationLayer != null && mMarkLayer != null) {
117 | if (mProgressDialog.isShowing()) {
118 | mProgressDialog.dismiss();
119 | beginDirection = true;
120 | }
121 | if (mapMode == MAP_FIXED) {
122 | mLocationLayer.setCurrentPosition(mPointF);
123 | }
124 | mLocationLayer.setRangeIndicatorMeters(location.getAccuracy());
125 | mapView.refresh();
126 |
127 | }
128 | }
129 | }
130 |
131 | /**
132 | * 返回校准状态值
133 | * @param provider provider
134 | * @param status status
135 | * @param extras
136 | * CALIBRATION_POOR = 0;
137 | * CALIBRATION_GOOD = 1;
138 | * CALIBRATION_EXCELLENT = 2;
139 | * STATUS_OUT_OF_SERVICE = 0;
140 | * STATUS_TEMPORARILY_UNAVAILABLE = 1;
141 | * STATUS_AVAILABLE = 2;
142 | * STATUS_LIMITED = 10;
143 | * STATUS_CALIBRATION_CHANGED = 11;
144 | */
145 | @Override
146 | public void onStatusChanged(String provider, int status, Bundle extras) {
147 | super.onStatusChanged(provider, status, extras);
148 | // Logger.w(status + "");
149 | }
150 | };
151 |
152 | private IARegion.Listener mRegionListener = new IARegion.Listener() {
153 |
154 | @Override
155 | public void onEnterRegion(IARegion region) {
156 | //获取配置的floorId
157 | if (region.getType() == IARegion.TYPE_FLOOR_PLAN && !mapView.isMapLoadFinish()) {
158 | String id = region.getId();
159 | Toast.makeText(getApplicationContext(), id, Toast.LENGTH_SHORT).show();
160 | fetchFloorPlan(id);
161 | }
162 |
163 | }
164 |
165 | @Override
166 | public void onExitRegion(IARegion region) {
167 | // leaving a previously entered region
168 | }
169 |
170 | };
171 |
172 | @Override
173 | public void onCreate(Bundle savedInstanceState) {
174 | super.onCreate(savedInstanceState);
175 | setContentView(R.layout.activity_map_view);
176 | initView();
177 | mDownloadManager = (DownloadManager) getSystemService(Context.DOWNLOAD_SERVICE);
178 | mIALocationManager = IALocationManager.create(this);
179 | mFloorPlanManager = IAResourceManager.create(this);
180 | //获取传感器服务
181 | mSensorManager = (SensorManager) getSystemService(SENSOR_SERVICE);
182 | setFloorPlanId(getIntent().getStringExtra("floorPlanId"));
183 |
184 | }
185 |
186 | private void checkOutMagSensor() {
187 | // 获取全部传感器列表
188 | List sensors = CommonMethord.getSensorLists(this);
189 | for (Sensor item : sensors) {
190 | if (2 == item.getType()) {
191 | //如果是yamaha的地磁计,就给提示
192 | if (item.getVendor().toLowerCase().contains("Yamaha".toLowerCase())) {
193 | new AlertDialog.Builder(this)
194 | .setCancelable(false)
195 | .setMessage("系统检测到您当前手机传感器对室内定位效果支持较弱,是否继续使用?")
196 | .setPositiveButton("确定", new DialogInterface.OnClickListener() {
197 | @Override
198 | public void onClick(DialogInterface dialog, int which) {
199 | useIndoor = true;
200 | mProgressDialog.show();
201 | }
202 | })
203 | .setNegativeButton("退出", new DialogInterface.OnClickListener() {
204 | @Override
205 | public void onClick(DialogInterface dialog, int which) {
206 | finish();
207 | }
208 | })
209 | .create()
210 | .show();
211 | } else {
212 | useIndoor = true;
213 | mProgressDialog.show();
214 | }
215 | }
216 | }
217 | }
218 |
219 | @Override
220 | protected void onResume() {
221 | super.onResume();
222 | Log.w(TAG, "==============onResume===================");
223 | // starts receiving location updates
224 | mIALocationManager.requestLocationUpdates(IALocationRequest.create(), mLocationListener);
225 | //注册区域监听,加载地图
226 | mIALocationManager.registerRegionListener(mRegionListener);
227 | //注册方向传感器
228 | mSensorManager.registerListener(this, mSensorManager.getDefaultSensor(Sensor.TYPE_ORIENTATION), SensorManager.SENSOR_DELAY_NORMAL);
229 | registerReceiver(onComplete, new IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE));
230 | mapView.setRunning(true);
231 | }
232 |
233 | @Override
234 | protected void onPause() {
235 | super.onPause();
236 | Log.w(TAG, "==============onPause===================");
237 | mapView.setRunning(false);
238 | mIALocationManager.removeLocationUpdates(mLocationListener);
239 | mIALocationManager.unregisterRegionListener(mRegionListener);
240 | mSensorManager.unregisterListener(this);
241 | unregisterReceiver(onComplete);
242 | // Toast.makeText(getApplicationContext(), "定位中止", Toast.LENGTH_SHORT).show();
243 | }
244 |
245 | @Override
246 | protected void onDestroy() {
247 | super.onDestroy();
248 |
249 | mProgressDialog.dismiss();
250 | mIALocationManager.destroy();
251 | mFloorMap = null;
252 |
253 | }
254 |
255 | /**
256 | * 设置floorPlanId
257 | *
258 | * @param floorPlanId 楼层id
259 | */
260 | private void setFloorPlanId(String floorPlanId) {
261 | if (!TextUtils.isEmpty(floorPlanId)) {
262 | IALocation location = IALocation.from(IARegion.floorPlan(floorPlanId));
263 | mIALocationManager.setLocation(location);
264 | }
265 | }
266 |
267 | private void initView() {
268 | Logger.init(this.getClass().getName());
269 | mapView = (MapView) findViewById(R.id.map_view);
270 | mTextView = (TextView) findViewById(R.id.text_img);
271 | savePoints = (Button) findViewById(R.id.btn_save_point);
272 | showPoints = (Button) findViewById(R.id.btn_get_points);
273 | changeMode = (Button) findViewById(R.id.btn_change_display_mode);
274 | savePoints.setOnClickListener(this);
275 | showPoints.setOnClickListener(this);
276 | savePoints.setVisibility(View.INVISIBLE);
277 | showPoints.setVisibility(View.INVISIBLE);
278 | changeMode.setOnClickListener(this);
279 | changeMode.setVisibility(View.INVISIBLE);
280 | //默认地图固定
281 | mapMode = MAP_FIXED;
282 | mProgressDialog = new ProgressDialog(MapViewActivity.this);
283 | mProgressDialog.setMessage("初始化地图数据...");
284 | mProgressDialog.setCanceledOnTouchOutside(false);
285 | mProgressDialog.show();
286 | }
287 |
288 | @Override
289 | public void onClick(View v) {
290 | switch (v.getId()) {
291 | // case R.id.btn_save_point:
292 | // //把坐标信息保存到根目录points.txt文件
293 | // boolean isSave = CommonMethord.saveFile(CommonMethord.ListToStr(mPointXYList));
294 | // if (isSave) {
295 | // Toast.makeText(getApplicationContext(), "坐标点保存成功", Toast.LENGTH_SHORT).show();
296 | // } else {
297 | // Toast.makeText(getApplicationContext(), "坐标点保存失败", Toast.LENGTH_SHORT).show();
298 | // }
299 | // break;
300 | // case R.id.btn_get_points:
301 | // String pointsData = CommonMethord.getFile(CommonMethord.getCurrentTime());
302 | // new AlertDialog.Builder(MapViewActivity.this)
303 | // .setTitle("points data")
304 | // .setMessage(pointsData)
305 | // .create()
306 | // .show();
307 | // break;
308 | case R.id.btn_change_display_mode:
309 | if (mapMode == MAP_FIXED) {
310 | Toast toast = Toast.makeText(this, "功能调试中", Toast.LENGTH_SHORT);
311 | toast.setGravity(Gravity.CENTER_VERTICAL, 0, 0);
312 | toast.show();
313 | mapMode = MAP_FOLLOW;
314 | changeMode.setText("切换为地图固定");
315 | //点居于整体中心
316 | mLocationLayer.setCurrentPosition(centerPoint);
317 | } else {
318 | mapMode = MAP_FIXED;
319 | mapView.setCurrentRotateDegrees(0);
320 | changeMode.setText("切换为地图跟随");
321 | }
322 | break;
323 | default:
324 | break;
325 | }
326 | }
327 |
328 | @Override
329 | public void onSensorChanged(SensorEvent event) {
330 | if (mapView != null && mLocationLayer != null) {
331 |
332 | if (event.sensor.getType() == Sensor.TYPE_ORIENTATION) {
333 | degree = event.values[0];
334 | }
335 | showPoints.setText(degree + "");
336 |
337 | if (mapMode == MAP_FIXED) {
338 | //设置指示器旋转角度
339 | mLocationLayer.setCompassIndicatorArrowRotateDegree(mapDegree + degree);
340 | }
341 | if (mapMode == MAP_FOLLOW) {
342 | //指针不动
343 | mLocationLayer.setCompassIndicatorArrowRotateDegree(0);
344 |
345 | //设置图片旋转
346 | mapView.setCurrentRotateDegrees(getTargetDircetion(degree) - mapDegree);
347 |
348 | }
349 | if (beginDirection) {
350 | mapView.refresh();
351 | }
352 | }
353 | }
354 |
355 | @Override
356 | public void onAccuracyChanged(Sensor sensor, int accuracy) {
357 |
358 | }
359 |
360 | /**
361 | * 修正方向传感器旋转角度
362 | *
363 | * @param degree 角度
364 | * @return 修正角
365 | */
366 | private float getTargetDircetion(float degree) {
367 | return (degree * -1.0f + 720) % 360;
368 | }
369 |
370 | /**
371 | * 加载地图
372 | *
373 | * @param filePath 文件路径
374 | */
375 | private List nodes;
376 | private List nodesContract;
377 | private List marks;
378 | private List marksName;
379 | private void showFloorPlanImage(String filePath) {
380 | // Logger.w("showFloorPlanImage: " + filePath + "; MetersToPixels=" + mFloorPlan.getMetersToPixels());
381 | mProgressDialog.setMessage("请移动方位以完成初始化操作");
382 | mFloorMap = BitmapFactory.decodeFile(filePath);
383 |
384 | nodes = TestData.getNodesList();
385 | nodesContract = TestData.getNodesContactList();
386 | marks = TestData.getMarks();
387 | marksName = TestData.getMarksName();
388 | MapUtils.init(nodes.size(), nodesContract.size());
389 |
390 | mapView.loadMap(mFloorMap);
391 | centerPoint = new PointF((float) mFloorMap.getWidth() / 2, (float) mFloorMap.getHeight() / 2);
392 | mapView.setScaleAndRotateTogether(true);
393 | mapView.setMapViewListener(new MapViewListener() {
394 | @Override
395 | public void onMapLoadSuccess() {
396 | if (mLocationLayer == null && mMarkLayer == null && mRouteLayer == null) {
397 | mLocationLayer = new LocationLayer(mapView);
398 | mMarkLayer = new MarkLayer(mapView, marks, marksName);
399 | mRouteLayer = new RouteLayer(mapView);
400 | // mLocationLayer.setCompassIndicatorArrowRotateDegree(0);
401 | mMarkLayer.setMarkIsClickListener(new MarkLayer.MarkIsClickListener() {
402 | @Override
403 | public void markIsClick(int num) {
404 | // Toast.makeText(getApplicationContext(),num+"",Toast.LENGTH_SHORT).show();
405 | if (num != -1){
406 | PointF target = new PointF(marks.get(num).x, marks.get(num).y);
407 | Toast.makeText(MapViewActivity.this,target.x+","+target.y,Toast.LENGTH_LONG).show();
408 | List routeList = MapUtils.getShortestDistanceBetweenTwoPoints
409 | (marks.get(0), target, nodes, nodesContract);
410 | mRouteLayer.setNodeList(nodes);
411 | mRouteLayer.setRouteList(routeList);
412 | mapView.refresh();
413 | }
414 | }
415 | });
416 | mapView.addLayer(mLocationLayer);
417 | mapView.addLayer(mMarkLayer);
418 | mapView.addLayer(mRouteLayer);
419 | mapView.refresh();
420 | Logger.i(">>>>>>>>>onMapLoadSuccess>>>>>");
421 | }
422 | }
423 |
424 | @Override
425 | public void onMapLoadFail() {
426 | Logger.e(">>>>>>>>>onMapLoadFail>>>>>");
427 | }
428 | });
429 |
430 | }
431 |
432 | /**
433 | * Broadcast receiver for floor plan image download
434 | */
435 | private BroadcastReceiver onComplete = new BroadcastReceiver() {
436 | @Override
437 | public void onReceive(Context context, Intent intent) {
438 | long id = intent.getLongExtra(DownloadManager.EXTRA_DOWNLOAD_ID, 0L);
439 | if (id != mDownloadId) {
440 | Logger.w("Ignore unrelated download");
441 | return;
442 | }
443 | Logger.w("Image download completed");
444 | final Bundle extras = intent.getExtras();
445 |
446 | DownloadManager.Query q = new DownloadManager.Query();
447 | q.setFilterById(extras.getLong(DownloadManager.EXTRA_DOWNLOAD_ID));
448 | Cursor c = mDownloadManager.query(q);
449 |
450 | if (c.moveToFirst()) {
451 | int status = c.getInt(c.getColumnIndex(DownloadManager.COLUMN_STATUS));
452 | if (status == DownloadManager.STATUS_SUCCESSFUL) {
453 | // process download
454 | String filePath = c.getString(c.getColumnIndex(DownloadManager.COLUMN_LOCAL_FILENAME));
455 | showFloorPlanImage(filePath);
456 | }
457 | }
458 | c.close();
459 | }
460 | };
461 |
462 | /**
463 | * Fetches floor plan data from IndoorAtlas server. Some room for cleaning up!!
464 | */
465 | private void fetchFloorPlan(String id) {
466 | cancelPendingNetworkCalls();
467 | final IATask asyncResult = mFloorPlanManager.fetchFloorPlanWithId(id);
468 | mPendingAsyncResult = asyncResult;
469 | if (mPendingAsyncResult != null) {
470 | mPendingAsyncResult.setCallback(new IAResultCallback() {
471 | @Override
472 | public void onResult(IAResult result) {
473 | Logger.w("fetch floor plan result:" + result);
474 | if (result.isSuccess() && result.getResult() != null) {
475 | mFloorPlan = result.getResult();
476 | String fileName = mFloorPlan.getId() + ".jpg";
477 | filePath = Environment.getExternalStorageDirectory() + "/"
478 | + Environment.DIRECTORY_DOWNLOADS + "/" + fileName;
479 | File file = new File(filePath);
480 | if (!file.exists()) {
481 | Logger.w("file not exists,Let's download");
482 | DownloadManager.Request request = new DownloadManager.Request(Uri.parse(mFloorPlan.getUrl()));
483 | request.setDescription("IndoorAtlas floor plan");
484 | request.setTitle("Floor plan");
485 | // requires android 3.2 or later to compile
486 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
487 | request.allowScanningByMediaScanner();
488 | request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_HIDDEN);
489 | }
490 | request.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, fileName);
491 |
492 | mDownloadId = mDownloadManager.enqueue(request);
493 | } else {
494 | showFloorPlanImage(filePath);
495 | }
496 | } else {
497 | // do something with error
498 | if (!asyncResult.isCancelled()) {
499 | Toast.makeText(getApplicationContext(),
500 | (result.getError() != null
501 | ? "error loading floor plan: " + result.getError()
502 | : "access to floor plan denied"),
503 | Toast.LENGTH_LONG).show();
504 | }
505 | }
506 | }
507 | }, Looper.getMainLooper()); // deliver callbacks in main thread Looper.getMainLooper()
508 | }
509 | }
510 |
511 | private void cancelPendingNetworkCalls() {
512 | if (mPendingAsyncResult != null && !mPendingAsyncResult.isCancelled()) {
513 | mPendingAsyncResult.cancel();
514 | }
515 | }
516 |
517 | @Override
518 | public void onBackPressed() {
519 | super.onBackPressed();
520 | }
521 | }
522 |
--------------------------------------------------------------------------------