From 5f0add31a3d5185c2aef0b50f212cf67150a8257 Mon Sep 17 00:00:00 2001 From: Alexey Kulish Date: Sun, 6 Nov 2016 17:34:59 +0300 Subject: [PATCH] Fix #3156 --- .../src/net/osmand/map/OsmandRegions.java | 78 ++++++-- .../net/osmand/plus/download/CityItem.java | 31 ++++ .../plus/download/ui/ItemViewHolder.java | 24 ++- .../download/ui/SearchDialogFragment.java | 175 +++++++++++++++++- 4 files changed, 282 insertions(+), 26 deletions(-) create mode 100644 OsmAnd/src/net/osmand/plus/download/CityItem.java diff --git a/OsmAnd-java/src/net/osmand/map/OsmandRegions.java b/OsmAnd-java/src/net/osmand/map/OsmandRegions.java index 3ab8ea2e38..7ab3b606e5 100644 --- a/OsmAnd-java/src/net/osmand/map/OsmandRegions.java +++ b/OsmAnd-java/src/net/osmand/map/OsmandRegions.java @@ -1,8 +1,18 @@ package net.osmand.map; -import gnu.trove.iterator.TIntObjectIterator; -import gnu.trove.list.array.TIntArrayList; +import net.osmand.PlatformUtil; +import net.osmand.ResultMatcher; +import net.osmand.binary.BinaryMapDataObject; +import net.osmand.binary.BinaryMapIndexReader; +import net.osmand.binary.BinaryMapIndexReader.MapIndex; +import net.osmand.binary.BinaryMapIndexReader.TagValuePair; +import net.osmand.data.LatLon; +import net.osmand.data.QuadRect; +import net.osmand.data.QuadTree; +import net.osmand.util.Algorithms; +import net.osmand.util.MapAlgorithms; +import net.osmand.util.MapUtils; import java.io.File; import java.io.IOException; @@ -21,19 +31,8 @@ import java.util.Map; import java.util.Set; import java.util.TreeSet; -import net.osmand.PlatformUtil; -import net.osmand.ResultMatcher; -import net.osmand.binary.BinaryMapDataObject; -import net.osmand.binary.BinaryMapIndexReader; -import net.osmand.binary.BinaryMapIndexReader.MapIndex; -import net.osmand.binary.BinaryMapIndexReader.TagValuePair; -import net.osmand.data.LatLon; -import net.osmand.data.QuadRect; -import net.osmand.data.QuadTree; -import net.osmand.osm.edit.Node; -import net.osmand.util.Algorithms; -import net.osmand.util.MapAlgorithms; -import net.osmand.util.MapUtils; +import gnu.trove.iterator.TIntObjectIterator; +import gnu.trove.list.array.TIntArrayList; public class OsmandRegions { @@ -686,4 +685,53 @@ public class OsmandRegions { } } + public BinaryMapDataObject findBinaryMapDataObject(LatLon latLon) { + int point31x = MapUtils.get31TileNumberX(latLon.getLongitude()); + int point31y = MapUtils.get31TileNumberY(latLon.getLatitude()); + + BinaryMapDataObject res = null; + List mapDataObjects = null; + try { + mapDataObjects = queryBbox(point31x, point31x, point31y, point31y); + } catch (IOException e) { + e.printStackTrace(); + } + + if (mapDataObjects != null) { + Iterator it = mapDataObjects.iterator(); + while (it.hasNext()) { + BinaryMapDataObject o = it.next(); + if (o.getTypes() != null) { + boolean isRegion = true; + for (int i = 0; i < o.getTypes().length; i++) { + TagValuePair tp = o.getMapIndex().decodeType(o.getTypes()[i]); + if ("boundary".equals(tp.value)) { + isRegion = false; + break; + } + } + WorldRegion downloadRegion = getRegionData(getFullName(o)); + if (!isRegion + || downloadRegion == null + || !downloadRegion.isRegionMapDownload() + || !contain(o, point31x, point31y)) { + it.remove(); + } + } + } + double smallestArea = -1; + for (BinaryMapDataObject o : mapDataObjects) { + double area = OsmandRegions.getArea(o); + if (smallestArea == -1) { + smallestArea = area; + res = o; + } else if (area < smallestArea) { + smallestArea = area; + res = o; + } + } + } + return res; + } + } diff --git a/OsmAnd/src/net/osmand/plus/download/CityItem.java b/OsmAnd/src/net/osmand/plus/download/CityItem.java new file mode 100644 index 0000000000..f101d49615 --- /dev/null +++ b/OsmAnd/src/net/osmand/plus/download/CityItem.java @@ -0,0 +1,31 @@ +package net.osmand.plus.download; + +import net.osmand.data.Amenity; + +public class CityItem { + private String name; + private Amenity amenity; + private IndexItem indexItem; + + public CityItem(String name, Amenity amenity, IndexItem indexItem) { + this.name = name; + this.amenity = amenity; + this.indexItem = indexItem; + } + + public String getName() { + return name; + } + + public Amenity getAmenity() { + return amenity; + } + + public IndexItem getIndexItem() { + return indexItem; + } + + public void setIndexItem(IndexItem indexItem) { + this.indexItem = indexItem; + } +} diff --git a/OsmAnd/src/net/osmand/plus/download/ui/ItemViewHolder.java b/OsmAnd/src/net/osmand/plus/download/ui/ItemViewHolder.java index db04a38731..417d323ba2 100644 --- a/OsmAnd/src/net/osmand/plus/download/ui/ItemViewHolder.java +++ b/OsmAnd/src/net/osmand/plus/download/ui/ItemViewHolder.java @@ -25,6 +25,7 @@ import net.osmand.plus.R; import net.osmand.plus.Version; import net.osmand.plus.activities.LocalIndexHelper.LocalIndexType; import net.osmand.plus.activities.LocalIndexInfo; +import net.osmand.plus.download.CityItem; import net.osmand.plus.download.DownloadActivity; import net.osmand.plus.download.DownloadActivityType; import net.osmand.plus.download.DownloadResourceGroup; @@ -33,6 +34,7 @@ import net.osmand.plus.download.IndexItem; import net.osmand.plus.download.ui.LocalIndexesFragment.LocalIndexOperationTask; import net.osmand.plus.helpers.FileNameTranslationHelper; import net.osmand.plus.srtmplugin.SRTMPlugin; +import net.osmand.util.Algorithms; import java.io.File; import java.text.DateFormat; @@ -130,6 +132,10 @@ public class ItemViewHolder { } public void bindIndexItem(final IndexItem indexItem) { + bindIndexItem(indexItem, null); + } + + public void bindIndexItem(final IndexItem indexItem, final String cityName) { initAppStatusVariables(); boolean isDownloading = context.getDownloadThread().isDownloading(indexItem); int progress = -1; @@ -138,11 +144,14 @@ public class ItemViewHolder { } boolean disabled = checkDisabledAndClickAction(indexItem); /// name and left item + String name; if(showTypeInName) { - nameTextView.setText(indexItem.getType().getString(context)); + name = indexItem.getType().getString(context); } else { - nameTextView.setText(indexItem.getVisibleName(context, context.getMyApplication().getRegions(), showParentRegionName)); + name = indexItem.getVisibleName(context, context.getMyApplication().getRegions(), showParentRegionName); } + String text = (!Algorithms.isEmpty(cityName) && !cityName.equals(name) ? cityName + "\n" : "") + name; + nameTextView.setText(text); if(!disabled) { nameTextView.setTextColor(textColorPrimary); } else { @@ -209,6 +218,17 @@ public class ItemViewHolder { } } + public void bindIndexItem(final CityItem cityItem) { + if (cityItem.getIndexItem() != null) { + bindIndexItem(cityItem.getIndexItem(), cityItem.getName()); + } else { + nameTextView.setText(cityItem.getName()); + nameTextView.setTextColor(textColorPrimary); + leftImageView.setImageDrawable(getContentIcon(context, R.drawable.ic_map)); + descrTextView.setVisibility(View.GONE); + progressBar.setVisibility(View.GONE); + } + } protected void download(IndexItem indexItem, DownloadResourceGroup parentOptional) { boolean handled = false; diff --git a/OsmAnd/src/net/osmand/plus/download/ui/SearchDialogFragment.java b/OsmAnd/src/net/osmand/plus/download/ui/SearchDialogFragment.java index 1eb94594c4..76fe94a18a 100644 --- a/OsmAnd/src/net/osmand/plus/download/ui/SearchDialogFragment.java +++ b/OsmAnd/src/net/osmand/plus/download/ui/SearchDialogFragment.java @@ -1,12 +1,14 @@ package net.osmand.plus.download.ui; import android.content.res.TypedArray; +import android.os.AsyncTask; import android.os.Bundle; import android.support.v4.app.DialogFragment; import android.support.v4.content.ContextCompat; import android.support.v7.view.ContextThemeWrapper; import android.support.v7.widget.SearchView; import android.support.v7.widget.Toolbar; +import android.util.Log; import android.util.TypedValue; import android.view.LayoutInflater; import android.view.View; @@ -23,11 +25,19 @@ import android.widget.RelativeLayout; import android.widget.RelativeLayout.LayoutParams; import net.osmand.Collator; +import net.osmand.CollatorStringMatcher; import net.osmand.OsmAndCollator; +import net.osmand.ResultMatcher; +import net.osmand.binary.BinaryMapDataObject; +import net.osmand.binary.BinaryMapIndexReader; +import net.osmand.binary.BinaryMapIndexReader.SearchRequest; +import net.osmand.data.Amenity; import net.osmand.map.OsmandRegions; +import net.osmand.map.WorldRegion; import net.osmand.plus.OsmandApplication; import net.osmand.plus.OsmandSettings; import net.osmand.plus.R; +import net.osmand.plus.download.CityItem; import net.osmand.plus.download.DownloadActivity; import net.osmand.plus.download.DownloadActivity.BannerAndDownloadFreeVersion; import net.osmand.plus.download.DownloadActivityType; @@ -36,9 +46,15 @@ import net.osmand.plus.download.DownloadResourceGroup; import net.osmand.plus.download.DownloadResourceGroup.DownloadResourceGroupType; import net.osmand.plus.download.DownloadResources; import net.osmand.plus.download.IndexItem; +import net.osmand.search.core.SearchPhrase; import net.osmand.util.Algorithms; +import java.io.File; +import java.io.IOException; +import java.io.RandomAccessFile; +import java.lang.ref.WeakReference; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; import java.util.Comparator; import java.util.LinkedList; @@ -196,7 +212,9 @@ public class SearchDialogFragment extends DialogFragment implements DownloadEven public void updateSearchText(String searchText) { this.searchText = searchText; - listAdapter.getFilter().filter(searchText); + SearchListAdapter.SearchIndexFilter filter = (SearchListAdapter.SearchIndexFilter) listAdapter.getFilter(); + filter.cancelFilter(); + filter.filter(searchText); } private OsmandApplication getMyApplication() { @@ -234,12 +252,14 @@ public class SearchDialogFragment extends DialogFragment implements DownloadEven private class SearchListAdapter extends BaseAdapter implements Filterable { private SearchIndexFilter mFilter; + private OsmandRegions osmandRegions; private List items = new LinkedList<>(); private DownloadActivity ctx; public SearchListAdapter(DownloadActivity ctx) { this.ctx = ctx; + this.osmandRegions = ctx.getMyApplication().getRegions(); TypedArray ta = ctx.getTheme().obtainStyledAttributes(new int[]{android.R.attr.textColorPrimary}); ta.recycle(); } @@ -267,7 +287,7 @@ public class SearchDialogFragment extends DialogFragment implements DownloadEven @Override public int getItemViewType(int position) { Object obj = items.get(position); - if (obj instanceof IndexItem) { + if (obj instanceof IndexItem || obj instanceof CityItem) { return 0; } else { return 1; @@ -282,9 +302,8 @@ public class SearchDialogFragment extends DialogFragment implements DownloadEven @Override public View getView(final int position, View convertView, ViewGroup parent) { final Object obj = items.get(position); - if (obj instanceof IndexItem) { + if (obj instanceof IndexItem || obj instanceof CityItem) { - IndexItem item = (IndexItem) obj; ItemViewHolder viewHolder; if (convertView != null && convertView.getTag() instanceof ItemViewHolder) { viewHolder = (ItemViewHolder) convertView.getTag(); @@ -295,8 +314,17 @@ public class SearchDialogFragment extends DialogFragment implements DownloadEven viewHolder.setShowRemoteDate(true); convertView.setTag(viewHolder); } - viewHolder.setShowTypeInDesc(true); - viewHolder.bindIndexItem(item); + if (obj instanceof IndexItem) { + IndexItem item = (IndexItem) obj; + viewHolder.setShowTypeInDesc(true); + viewHolder.bindIndexItem(item); + } else { + CityItem item = (CityItem) obj; + viewHolder.bindIndexItem(item); + if (item.getIndexItem() == null) { + new IndexItemResolverTask(viewHolder, item).execute(); + } + } } else { DownloadResourceGroup group = (DownloadResourceGroup) obj; DownloadGroupViewHolder viewHolder; @@ -327,14 +355,64 @@ public class SearchDialogFragment extends DialogFragment implements DownloadEven return mFilter; } + class IndexItemResolverTask extends AsyncTask { + private final WeakReference viewHolderReference; + private final CityItem cityItem; + + public IndexItemResolverTask(ItemViewHolder viewHolder, CityItem cityItem) { + this.viewHolderReference = new WeakReference<>(viewHolder); + this.cityItem = cityItem; + } + + @Override + protected IndexItem doInBackground(Void... params) { + Amenity amenity = cityItem.getAmenity(); + BinaryMapDataObject o = osmandRegions.findBinaryMapDataObject(amenity.getLocation()); + if (o != null) { + String selectedFullName = osmandRegions.getFullName(o); + WorldRegion downloadRegion = osmandRegions.getRegionData(selectedFullName); + List indexItems = ctx.getDownloadThread().getIndexes().getIndexItems(downloadRegion); + for (IndexItem item : indexItems) { + if (item.getType() == DownloadActivityType.NORMAL_FILE) { + return item; + } + } + } + return null; + } + + @Override + protected void onPostExecute(IndexItem indexItem) { + if (isCancelled()) { + return; + } + ItemViewHolder viewHolder = viewHolderReference.get(); + if (viewHolder != null) { + if (indexItem != null) { + cityItem.setIndexItem(indexItem); + viewHolder.bindIndexItem(indexItem, cityItem.getName()); + } + } + } + } + private final class SearchIndexFilter extends Filter { private OsmandRegions osmandRegions; + private final int searchCityLimit = 10000; + private final List citySubTypes = Arrays.asList("city", "town"); + private SearchRequest searchCityRequest; public SearchIndexFilter() { this.osmandRegions = ctx.getMyApplication().getRegions(); } + public void cancelFilter() { + if (searchCityRequest != null) { + searchCityRequest.setInterrupted(true); + } + } + private void processGroup(DownloadResourceGroup group, List filter, List> conds) { String name = null; if (group.getRegion() != null && group.getRegion().getRegionSearchText() != null) { @@ -387,6 +465,63 @@ public class SearchDialogFragment extends DialogFragment implements DownloadEven } } + public List searchCities(final OsmandApplication app, final String text) throws IOException { + IndexItem worldBaseMapItem = app.getDownloadThread().getIndexes().getWorldBaseMapItem(); + if (worldBaseMapItem == null || !worldBaseMapItem.isDownloaded()) { + return new ArrayList<>(); + } + File obf = worldBaseMapItem.getTargetFile(app); + final BinaryMapIndexReader baseMapReader = new BinaryMapIndexReader(new RandomAccessFile(obf, "r"), obf); + final SearchPhrase.NameStringMatcher nm = new SearchPhrase.NameStringMatcher( + text, CollatorStringMatcher.StringMatcherMode.CHECK_STARTS_FROM_SPACE); + final String lang = app.getSettings().MAP_PREFERRED_LOCALE.get(); + final boolean translit = app.getSettings().MAP_TRANSLITERATE_NAMES.get(); + final List amenities = new ArrayList<>(); + SearchRequest request = BinaryMapIndexReader.buildSearchPoiRequest( + 0, 0, + text, + Integer.MIN_VALUE, Integer.MAX_VALUE, + Integer.MIN_VALUE, Integer.MAX_VALUE, + new ResultMatcher() { + int count = 0; + + @Override + public boolean publish(Amenity amenity) { + if (count++ > searchCityLimit) { + return false; + } + List otherNames = amenity.getAllNames(true); + String localeName = amenity.getName(lang, translit); + String subType = amenity.getSubType(); + if (!citySubTypes.contains(subType) + || (!nm.matches(localeName) && !nm.matches(otherNames))) { + return false; + } + amenities.add(amenity); + return false; + } + + @Override + public boolean isCancelled() { + return count > searchCityLimit; + } + }); + + searchCityRequest = request; + baseMapReader.searchPoiByName(request); + try { + baseMapReader.close(); + } catch (Exception e) { + e.printStackTrace(); + } + + List items = new ArrayList<>(); + for (Amenity amenity : amenities) { + items.add(new CityItem(amenity.getName(), amenity, null)); + } + return items; + } + @Override protected FilterResults performFiltering(CharSequence constraint) { DownloadResources root = ctx.getDownloadThread().getIndexes(); @@ -396,6 +531,15 @@ public class SearchDialogFragment extends DialogFragment implements DownloadEven results.values = new ArrayList<>(); results.count = 0; } else { + List filter = new ArrayList<>(); + if (constraint.length() > 3) { + try { + filter.addAll(searchCities(getMyApplication(), constraint.toString())); + } catch (IOException e) { + e.printStackTrace(); + } + } + String[] ors = constraint.toString().split(","); List> conds = new ArrayList<>(); for (String or : ors) { @@ -411,7 +555,6 @@ public class SearchDialogFragment extends DialogFragment implements DownloadEven } } - List filter = new ArrayList<>(); processGroup(root, filter, conds); final Collator collator = OsmAndCollator.primaryCollator(); @@ -422,13 +565,27 @@ public class SearchDialogFragment extends DialogFragment implements DownloadEven String str2; if (obj1 instanceof DownloadResourceGroup) { str1 = ((DownloadResourceGroup) obj1).getName(ctx); - } else { + } else if (obj1 instanceof IndexItem) { str1 = ((IndexItem) obj1).getVisibleName(getMyApplication(), osmandRegions, false); + } else { + Amenity a = ((CityItem) obj1).getAmenity(); + if ("city".equals(a.getSubType())) { + str1 = "!" + ((CityItem) obj1).getName(); + } else { + str1 = ((CityItem) obj1).getName(); + } } if (obj2 instanceof DownloadResourceGroup) { str2 = ((DownloadResourceGroup) obj2).getName(ctx); - } else { + } else if (obj2 instanceof IndexItem) { str2 = ((IndexItem) obj2).getVisibleName(getMyApplication(), osmandRegions, false); + } else { + Amenity a = ((CityItem) obj2).getAmenity(); + if ("city".equals(a.getSubType())) { + str2 = "!" + ((CityItem) obj2).getName(); + } else { + str2 = ((CityItem) obj2).getName(); + } } return collator.compare(str1, str2); }