我有一个应用程序,在这个应用程序中,我使用谷歌游戏地图的新服务API不断跟踪用户的位置。我每秒都在更新位置,我也在检查用户活动,如静止、倾斜、IN车辆等,以获得更好的位置跟踪。我能够使用我的代码绘制路径,但它不准确,并且与用户实际驾驶/行走的道路大不相同。它总是在远离实际用户路径的地方画线。
我的服务:-
public class MyService extends Service implements LocationListener {
private final Context mContext;
private static MyService mInstance = null;
private final static String TAG = "RidingTimerService";
boolean isGPSEnabled = false;
private long mStartTime = 0L;
private long timeInMilliseconds = 0L;
private long timeSwapBuff = 0L;
private long updatedTime = 0L;
private Handler mHandler = null;
private int mHours = 0;
// Declaring a Location Manager
private LocationManager mLocationManager = null;
private Location mCurrentLocation = null; // location
private double[][] positions;
private long[] times;
private final Integer data_points = 2; // how many data points to calculate
private double mTravelledDistance = 0.0f;
private SharedPreferences mPreferences = null;
private ArrayList<LatLng> mDirectionsPoints = new ArrayList<LatLng>();
public boolean mIsPhoneMoving = false;
public int mActivityType = -1;
int counter = 0;
private IntentFilter mBroadcastFilter;
private DetectionRequester mDetectionRequester;
private DetectionRemover mDetectionRemover;
public MyService() {
mContext = this;
}
@Override
public void onCreate() {
super.onCreate();
mInstance = this;
// two arrays for position and time.
positions = new double[data_points][2];
times = new long[data_points];
mStartTime = SystemClock.uptimeMillis();
mHandler = new Handler();
mHandler.postDelayed(countDownTimerThread, 0);
mPreferences = getSharedPreferences(MyPreferences.PREFERENCES,
Context.MODE_PRIVATE);
// Create a new Intent filter for the broadcast receiver
mBroadcastFilter = new IntentFilter(
MyConstants.ACTION_REFRESH_STATUS_LIST);
mBroadcastFilter.addCategory(MyConstants.CATEGORY_LOCATION_SERVICES);
// Get detection requester and remover objects
mDetectionRequester = new DetectionRequester(this);
mDetectionRemover = new DetectionRemover(this);
mDirectionsPoints.clear();
}
/**
* Pause the timer
*/
public void pauseRide() {
timeSwapBuff += timeInMilliseconds;
mHandler.removeCallbacks(countDownTimerThread);
mLocationManager.removeUpdates(this);
}
/**
* Restart the timer
*/
public void reStartRide() {
mStartTime = SystemClock.uptimeMillis();
mHandler.postDelayed(countDownTimerThread, 0);
mLocationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER,
0, 0, this);
}
@Override
public void onDestroy() {
mHandler.removeCallbacks(countDownTimerThread);
mLocationManager.removeUpdates(this);
super.onDestroy();
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
getLocation();
return super.onStartCommand(intent, flags, startId);
}
/**
* Timer thread
*/
private final Runnable countDownTimerThread = new Runnable() {
public void run() {
mHours = 0;
timeInMilliseconds = SystemClock.uptimeMillis() - mStartTime;
updatedTime = timeSwapBuff + timeInMilliseconds;
int secs = (int) (updatedTime / 1000);
final int mins = secs / 60;
mHours = mins / 60;
secs = secs % 60;
final Message msg = new Message();
final Bundle bundle = new Bundle();
bundle.putInt(MyPreferences.BROADCAST_CODES, 300);
bundle.putString(
"time",
String.format("%02d", mHours) + ":"
+ String.format("%02d", mins) + ":"
+ String.format("%02d", secs));
msg.setData(bundle);
final Intent intent = new Intent(MyConstants.BROADCAST_INTENT);
intent.putExtras(bundle);
sendBroadcast(intent);
mHandler.postDelayed(this, 1000);
}
};
@Override
public IBinder onBind(final Intent intent) {
return null;
}
@Override
public void onLocationChanged(final Location location) {
if (location != null) {
LocationUtils.sLatitude = location.getLatitude();
LocationUtils.sLongitude = location.getLongitude();
mDirectionsPoints.add(new LatLng(LocationUtils.sLatitude,
LocationUtils.sLongitude));
riderLocation(location);
} else {
Log.i(TAG, "Location is not available.");
}
}
/**
* Calculate speed, distance and average speed. Send Broadcast which
* received by activities
*
* @param location
*/
private void riderLocation(final Location location) {
DecimalFormat formatter = new DecimalFormat("#0.00");
double distance = 0.0;
Double speed = 0.0;
long t1 = 0l;
final float[] results = new float[3];
positions[counter][0] = location.getLatitude();
positions[counter][1] = location.getLongitude();
times[counter] = location.getTime();
final Bundle bundle = new Bundle();
bundle.putInt(MyPreferences.BROADCAST_CODES, 200);
distance = calculateDistance(positions[counter][0],
positions[counter][1], positions[(counter + (data_points - 1))
% data_points][0],
positions[(counter + (data_points - 1)) % data_points][1]);
Location.distanceBetween(positions[counter][0], positions[counter][1],
positions[(counter + (data_points - 1)) % data_points][0],
positions[(counter + (data_points - 1)) % data_points][1],
results);
mTravelledDistance += results[0] / 1000;
LocationUtils.sDistance = formatter
.format((mTravelledDistance * 100.0) / 100.0);
final double averageSpeed = mTravelledDistance / mHours;
LocationUtils.sAverageSpeed = formatter
.format((averageSpeed * 100.0) / 100.0);
if (location.hasSpeed()) {
speed = location.getSpeed() * 3.6;
LocationUtils.sSpeed = speed.intValue();
} else {
try {
t1 = times[counter]
- times[(counter + (data_points - 1)) % data_points];
} catch (final NullPointerException e) {
// all good, just not enough data yet.
}
speed = (distance / t1) * 3.6;
LocationUtils.sSpeed = speed.intValue();
counter = (counter + 1) % data_points;
}
LocationUtils.sLatitude = location.getLatitude();
LocationUtils.sLongitude = location.getLongitude();
final Intent intent = new Intent(MyConstants.BROADCAST_INTENT);
intent.putExtras(bundle);
sendBroadcast(intent);
}
public void onProviderDisabled(final String arg0) {
Toast.makeText(getApplicationContext(), "Gps Disabled",
Toast.LENGTH_LONG).show();
}
public void onProviderEnabled(final String arg0) {
Toast.makeText(getApplicationContext(), "Gps Enabled",
Toast.LENGTH_SHORT).show();
}
/**
* @return location
*/
public Location getLocation() {
try {
mLocationManager = (LocationManager) getSystemService(LOCATION_SERVICE);
// getting GPS status
isGPSEnabled = mLocationManager
.isProviderEnabled(LocationManager.GPS_PROVIDER);
if (!isGPSEnabled) {
// no network provider is enabled
showSettingsAlert();
} else {
// if GPS Enabled get lat/long using GPS Services
if (isGPSEnabled) {
mLocationManager.requestLocationUpdates(
LocationManager.GPS_PROVIDER, 0, 0, this);
Log.d(TAG, "GPS Enabled");
if (mCurrentLocation == null) {
if (mLocationManager != null) {
mCurrentLocation = mLocationManager
.getLastKnownLocation(LocationManager.GPS_PROVIDER);
if (mCurrentLocation != null) {
LocationUtils.sLatitude = mCurrentLocation
.getLatitude();
LocationUtils.sLongitude = mCurrentLocation
.getLongitude();
}
}
} else {
LocationUtils.sLatitude = mCurrentLocation
.getLatitude();
LocationUtils.sLongitude = mCurrentLocation
.getLongitude();
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
return mCurrentLocation;
}
/**
* @fn public static RidingTimerService getInstance()
* @brief returns instance of the service.
* @return RidingTimerService instance
*/
public static MyService getInstance() {
return mInstance;
}
@Override
public void onStatusChanged(String provider, int status, Bundle extras) {
}
/**
* Reset all the values stored in Preferences
*/
public void resetAllText() {
mPreferences.edit().putString(MyPreferences.LATITUDE, "0.0").commit();
mPreferences.edit().putString(MyPreferences.LONGITUDE, "0.0")
.commit();
mPreferences.edit().putString(MyPreferences.SPEED, "0.0").commit();
mPreferences.edit().putString(MyPreferences.AVERAGE_SPEED, "0.0")
.commit();
mPreferences.edit().putString(MyPreferences.DISTANCE, "0.0").commit();
}
/**
* Respond to "Start" button by requesting activity recognition updates.
*
* @param view
* The view that triggered this method.
*/
public void onStartUpdates() {
// Pass the update request to the requester object
mDetectionRequester.requestUpdates();
}
/**
* Respond to "Stop" button by canceling updates.
*
* @param view
* The view that triggered this method.
*/
public void onStopUpdates() {
// Pass the remove request to the remover object
mDetectionRemover.removeUpdates(mDetectionRequester
.getRequestPendingIntent());
/*
* Cancel the PendingIntent. Even if the removal request fails,
* canceling the PendingIntent will stop the updates.
*/
mDetectionRequester.getRequestPendingIntent().cancel();
}
/**
* Function to show settings alert dialog On pressing Settings button will
* lauch Settings Options
* */
public void showSettingsAlert() {
AlertDialog.Builder alertDialog = new AlertDialog.Builder(mContext);
alertDialog.setTitle("GPS");
alertDialog.setMessage("GPS is not enabled. Do you want to enable it?");
// On pressing Settings button
alertDialog.setPositiveButton("Settings",
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
Intent intent = new Intent(
Settings.ACTION_LOCATION_SOURCE_SETTINGS);
mContext.startActivity(intent);
}
});
// on pressing cancel button
alertDialog.setNegativeButton("Cancel",
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
dialog.cancel();
}
});
// Showing Alert Message
alertDialog.show();
}
/**
* @return List of Direction points
*/
public ArrayList<LatLng> getDirectionsPoints() {
if (mDirectionsPoints != null) {
return mDirectionsPoints;
} else {
mDirectionsPoints = new ArrayList<LatLng>();
return mDirectionsPoints;
}
}
/**
* Calculate distance
*
* @param lat1
* @param lon1
* @param lat2
* @param lon2
* @return
*/
private double calculateDistance(final double lat1, final double lon1,
final double lat2, final double lon2) {
// haversine great circle distance approximation, returns meters
final double theta = lon1 - lon2;
double dist = Math.sin(deg2rad(lat1)) * Math.sin(deg2rad(lat2))
+ Math.cos(deg2rad(lat1)) * Math.cos(deg2rad(lat2))
* Math.cos(deg2rad(theta));
dist = Math.acos(dist);
dist = rad2deg(dist);
dist = dist * 60; // 60 nautical miles per degree of seperation
dist = dist * 1852; // 1852 meters per nautical mile
return dist;
}
private double deg2rad(final double deg) {
return (deg * Math.PI / 180.0);
}
private double rad2deg(final double rad) {
return (rad * 180.0 / Math.PI);
}
}
我每1秒更新一次位置,每3秒更新一次活动识别。我能够获取位置,但在Google地图中没有正确绘制路径。
绘制路径的代码:-
public class MapsActivity extends FragmentActivity {
private static GoogleMap mGoogleMap = null;
private static final String TAG = "MapsActivity";
private Button mBtnStartRide, mBtnPauseRide, mBtnStopRide = null;
private static TextView mTxtLatLong, mTxtTimer, mTxtTotalSize,
mTxtSpeed = null;
private static PolylineOptions mPolyLineOptions = null;
// Storing the directions returned by the direcction api
private SharedPreferences mPreferences = null;
private MapsBroadcastReceiver receiver = null;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_maps);
receiver = new MapsBroadcastReceiver();
mPreferences = getSharedPreferences(TestPreferences.PREFERENCES,
Context.MODE_PRIVATE);
mTxtLatLong = (TextView) findViewById(R.id.txtLatLong);
mTxtTimer = (TextView) findViewById(R.id.txtTimer);
mTxtTotalSize = (TextView) findViewById(R.id.txtDirectionsSize);
mTxtSpeed = (TextView) findViewById(R.id.txtSpeed);
mBtnStartRide = (Button) findViewById(R.id.btn_start_ride);
mBtnPauseRide = (Button) findViewById(R.id.btn_pause_ride);
mBtnStopRide = (Button) findViewById(R.id.btn_stop_ride);
final Button mBtnCenter = (Button) findViewById(R.id.btn_center);
mBtnCenter.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
animateCameraTo();
}
});
mBtnStartRide.setOnClickListener(btnStartRideClickListener);
mBtnPauseRide.setOnClickListener(btnPauseRideClickListener);
mBtnStopRide.setOnClickListener(btnStopRideClickListener);
initilizeMap();
}
/**
* Start Ride Button Click Listener
*/
private OnClickListener btnStartRideClickListener = new OnClickListener() {
@Override
public void onClick(View v) {
if (mPreferences.getBoolean(TestPreferences.IS_RIDE_PAUSE, false)) {
if (MyService.getInstance() != null) {
MyService.getInstance().reStartRide();
}
} else {
startService(new Intent(MapsActivity.this,
MyService.class));
}
mPreferences.edit().putBoolean(TestPreferences.IS_RIDE_START, true)
.commit();
mPreferences.edit().remove(TestPreferences.IS_RIDE_PAUSE).commit();
mPreferences.edit().remove(TestPreferences.IS_RIDE_STOPPED)
.commit();
mBtnStartRide.setVisibility(View.GONE);
mBtnPauseRide.setVisibility(View.VISIBLE);
mBtnStopRide.setVisibility(View.VISIBLE);
}
};
/**
* Start Ride Button Click Listener
*/
private OnClickListener btnPauseRideClickListener = new OnClickListener() {
@Override
public void onClick(View v) {
MyService.getInstance().pauseRide();
mPreferences.edit().putBoolean(TestPreferences.IS_RIDE_PAUSE, true)
.commit();
mPreferences.edit()
.putBoolean(TestPreferences.IS_RIDE_START, false).commit();
mBtnStartRide.setVisibility(View.VISIBLE);
mBtnPauseRide.setVisibility(View.GONE);
mBtnStopRide.setVisibility(View.VISIBLE);
}
};
/**
* Stop Ride Button Click Listener
*/
private OnClickListener btnStopRideClickListener = new OnClickListener() {
@Override
public void onClick(View v) {
stopService(new Intent(MapsActivity.this, MyService.class));
mPreferences.edit().remove(TestPreferences.IS_RIDE_PAUSE).commit();
mPreferences.edit()
.putBoolean(TestPreferences.IS_RIDE_START, false).commit();
mPreferences.edit()
.putBoolean(TestPreferences.IS_RIDE_STOPPED, true).commit();
mBtnStartRide.setVisibility(View.VISIBLE);
mBtnPauseRide.setVisibility(View.GONE);
mBtnStopRide.setVisibility(View.GONE);
}
};
@Override
protected void onResume() {
super.onResume();
registerReceiver(receiver, new IntentFilter(
MyConstants.BROADCAST_INTENT));
initilizeMap();
if (mPreferences.getBoolean(TestPreferences.IS_RIDE_START, false)) {
mBtnStartRide.setVisibility(View.GONE);
mBtnPauseRide.setVisibility(View.VISIBLE);
mBtnStopRide.setVisibility(View.VISIBLE);
} else if (mPreferences
.getBoolean(TestPreferences.IS_RIDE_PAUSE, false)) {
mBtnStartRide.setVisibility(View.VISIBLE);
mBtnPauseRide.setVisibility(View.GONE);
mBtnStopRide.setVisibility(View.VISIBLE);
} else if (mPreferences.getBoolean(TestPreferences.IS_RIDE_STOPPED,
false)) {
// Show start button and gone Pause & Stop both
mBtnStartRide.setVisibility(View.VISIBLE);
mBtnPauseRide.setVisibility(View.GONE);
mBtnStopRide.setVisibility(View.GONE);
} else {
// Show start button and gone Pause & Stop both
mBtnStartRide.setVisibility(View.VISIBLE);
mBtnPauseRide.setVisibility(View.GONE);
mBtnStopRide.setVisibility(View.GONE);
}
setAllText();
}
@Override
protected void onPause() {
super.onPause();
unregisterReceiver(receiver);
}
@Override
protected void onDestroy() {
super.onDestroy();
mGoogleMap = null;
}
/**
* Function to load map. If map is not created it will create it for you
* */
private void initilizeMap() {
if (mGoogleMap == null) {
mGoogleMap = ((MapFragment) getFragmentManager().findFragmentById(
R.id.map)).getMap();
// check if map is created successfully or not
if (mGoogleMap == null) {
Toast.makeText(getApplicationContext(),
"Sorry! unable to create maps", Toast.LENGTH_SHORT)
.show();
} else {
mPolyLineOptions = new PolylineOptions().width(6).color(
Color.BLUE);
mGoogleMap.setTrafficEnabled(true);
mGoogleMap.getUiSettings().setMyLocationButtonEnabled(true);
mGoogleMap.getUiSettings().setRotateGesturesEnabled(true);
mGoogleMap.getUiSettings().setCompassEnabled(true);
mGoogleMap.getUiSettings().setZoomGesturesEnabled(true);
mGoogleMap.getUiSettings().setZoomControlsEnabled(true);
if (MyService.getInstance() != null
&& MyService.getInstance().getDirectionsPoints() != null
&& !MyService.getInstance().getDirectionsPoints()
.isEmpty()
&& MyService.getInstance().getDirectionsPoints()
.size() >= 1) {
mGoogleMap.addMarker(new MarkerOptions()
.position(
MyService.getInstance()
.getDirectionsPoints().get(0))
.anchor(0.8f, 1.0f).title("Your Location"));
animateCameraTo(MyService.getInstance()
.getDirectionsPoints().get(0).latitude,
MyService.getInstance().getDirectionsPoints()
.get(0).longitude);
}
}
} else {
mPolyLineOptions = new PolylineOptions().width(6).color(Color.BLUE);
mGoogleMap.getUiSettings().setMyLocationButtonEnabled(true);
mGoogleMap.getUiSettings().setRotateGesturesEnabled(true);
mGoogleMap.getUiSettings().setCompassEnabled(true);
mGoogleMap.getUiSettings().setZoomGesturesEnabled(true);
mGoogleMap.getUiSettings().setZoomControlsEnabled(true);
if (MyService.getInstance() != null
&& MyService.getInstance().getDirectionsPoints() != null
&& !MyService.getInstance().getDirectionsPoints()
.isEmpty()
&& MyService.getInstance().getDirectionsPoints().size() >= 1) {
Log.i(TAG, "Lat long in resume2222222222 = "
+ MyService.getInstance().getDirectionsPoints()
.get(0).latitude
+ ", "
+ MyService.getInstance().getDirectionsPoints()
.get(0).longitude);
mGoogleMap.addMarker(new MarkerOptions()
.position(
MyService.getInstance().getDirectionsPoints()
.get(0)).anchor(0.8f, 1.0f)
.title("Your Location"));
animateCameraTo(MyService.getInstance().getDirectionsPoints()
.get(0).latitude, MyService.getInstance()
.getDirectionsPoints().get(0).longitude);
}
}
}
/**
* @author Scorpion
*
*/
private class MapsBroadcastReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
switch (intent.getExtras().getInt(TestPreferences.BROADCAST_CODES)) {
case 100:
showErrorDialog(intent.getExtras().getInt(
TestPreferences.BROADCAST_CODES));
break;
case 200:
setAllText();
break;
case 300:
if (intent.getExtras().getString("time") != null) {
mTxtTimer.setText("Timer - "
+ intent.getExtras().getString("time"));
} else {
mTxtTimer.setText("Timer - 00:00:00");
}
break;
default:
break;
}
if (MyService.getInstance() != null
&& !MyService.getInstance().getDirectionsPoints()
.isEmpty()
&& MyService.getInstance().getDirectionsPoints().size() == 1) {
mGoogleMap.addMarker(new MarkerOptions()
.position(
MyService.getInstance().getDirectionsPoints()
.get(0)).anchor(0.8f, 1.0f)
.title("Your Location"));
}
}
}
/**
*
*/
public void setAllText() {
mTxtLatLong.setText("Lat - " + LocationUtils.sLatitude + ", Lng - "
+ LocationUtils.sLongitude);
mTxtSpeed.setText("Speed - " + LocationUtils.sSpeed);
mTxtTotalSize.setText("Distance - " + LocationUtils.sDistance);
}
/**
* Animate to position on Google Maps
*
* @param lat
* @param lng
*/
private void animateCameraTo() {
// Saving the points in a polyline
if (MyService.getInstance() != null
&& MyService.getInstance().getDirectionsPoints() != null
&& !MyService.getInstance().getDirectionsPoints().isEmpty()) {
mPolyLineOptions.geodesic(true);
mPolyLineOptions.addAll(MyService.getInstance()
.getDirectionsPoints());
// Drawing the path on the map Polyline route =
mGoogleMap.addPolyline(mPolyLineOptions);
int index = MyService.getInstance().getDirectionsPoints().size() - 1;
CameraPosition cameraPosition = new CameraPosition.Builder()
.target(MyService.getInstance().getDirectionsPoints()
.get(index)).zoom(20).build();
mGoogleMap.animateCamera(CameraUpdateFactory
.newCameraPosition(cameraPosition));
}
}
/**
* Animate to position on Google Maps
*
* @param lat
* @param lng
*/
private void animateCameraTo(final double lat, final double lng) {
// Saving the points in a polyline
if (MyService.getInstance() != null
&& MyService.getInstance().getDirectionsPoints() != null
&& !MyService.getInstance().getDirectionsPoints().isEmpty()) {
mPolyLineOptions.geodesic(true);
mPolyLineOptions.addAll(MyService.getInstance()
.getDirectionsPoints());
// Drawing the path on the map
mGoogleMap.addPolyline(mPolyLineOptions);
CameraPosition cameraPosition = new CameraPosition.Builder()
.target(new LatLng(lat, lng)).zoom(20).build();
mGoogleMap.animateCamera(CameraUpdateFactory
.newCameraPosition(cameraPosition));
}
}
/**
* Show a dialog returned by Google Play services for the connection error
* code
*
* @param errorCode
* An error code returned from onConnectionFailed
*/
private void showErrorDialog(int errorCode) {
// Get the error dialog from Google Play services
Dialog errorDialog = GooglePlayServicesUtil.getErrorDialog(errorCode,
MapsActivity.this,
LocationUtils.CONNECTION_FAILURE_RESOLUTION_REQUEST);
// If Google Play services can provide an error dialog
if (errorDialog != null) {
// Create a new DialogFragment in which to show the error dialog
ErrorDialogFragment errorFragment = new ErrorDialogFragment();
// Set the dialog in the DialogFragment
errorFragment.setDialog(errorDialog);
// Show the error dialog in the DialogFragment
errorFragment.show(getSupportFragmentManager(),
LocationUtils.APPTAG);
}
}
}
此代码将起作用
GoogleMap map;
// ... get a map.
// Add a thin red line from London to New York.
Polyline line = map.addPolyline(new PolylineOptions()
.add(new LatLng(51.5, -0.1), new LatLng(40.7, -74.0))
.width(5)
.color(Color.RED));
您应该将问题拆分为数据采集和数据展示字段。
因此,取两个你精确知道的坐标,在谷歌地图上画一条直线。如果它真的显示在错误的位置,谷歌地图确实有bug,这BTW我不相信。
然后打印出您的坐标,例如在绘制坐标之前打印到LogCat并检查坐标。如果它们是错误的,这不是GoogleMaps的问题,而是LocationProvider或您如何使用它的问题。
如果坐标收集正确,您可以从LogCat中以某种方式获取它们,并直接在代码中使用它们来绘制折线。如果折线被移位,您可能再次在GoogleMap中找到了bug。然后您应该将坐标粘贴在此处,以便有人可以复制它。它可能取决于设备。我从来没有一条折线与地图不匹配。
您绘制路径的代码看起来很奇怪。在for循环
中,您应该只将点添加到PolylineOptions
的实例(您在进入for循环之前创建的)。
然后,在for循环
之后,您可以将宽度
、颜色
等添加到multilineOptions
对象
中,最后在mGoogleMap. addPolyline()
中使用它。
在您的编码中,您以某种方式为每个点添加了一个新的折线
(我想知道这到底是不是画线,因为每个“线”只包含一个点。)。