Merge pull request #11505 from osmandapp/master

update test branch
This commit is contained in:
Hardy 2021-04-23 00:13:57 +02:00 committed by GitHub
commit d2a6cab080
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
23 changed files with 539 additions and 99 deletions

View file

@ -186,7 +186,7 @@ public class RouteColorize {
public List<RouteColorizationPoint> getResult(boolean simplify) {
List<RouteColorizationPoint> result = new ArrayList<>();
if (simplify) {
result = simplify();
result = simplify(zoom);
} else {
for (int i = 0; i < latitudes.length; i++) {
result.add(new RouteColorizationPoint(i, latitudes[i], longitudes[i], values[i]));
@ -200,7 +200,7 @@ public class RouteColorize {
public int getColorByValue(double value) {
if (Double.isNaN(value)) {
value = (minValue + maxValue) / 2;
value = colorizationType == ColorizationType.SLOPE ? minValue : (minValue + maxValue) / 2;
}
for (int i = 0; i < palette.length - 1; i++) {
if (value == palette[i][VALUE_INDEX])
@ -242,7 +242,7 @@ public class RouteColorize {
return rgbaToDecimal(0, 0, 0, 0);
}
private List<RouteColorizationPoint> simplify() {
public List<RouteColorizationPoint> simplify(int zoom) {
if (dataList == null) {
dataList = new ArrayList<>();
for (int i = 0; i < latitudes.length; i++) {
@ -266,6 +266,8 @@ public class RouteColorize {
List<RouteColorizationPoint> sublist = dataList.subList(prevId, currentId);
simplified.addAll(getExtremums(sublist));
}
Node lastSurvivedPoint = result.get(result.size() - 1);
simplified.add(dataList.get((int) lastSurvivedPoint.getId()));
return simplified;
}

View file

@ -5,8 +5,10 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="@dimen/content_padding"
android:paddingStart="@dimen/content_padding"
android:paddingLeft="@dimen/content_padding"
android:paddingTop="@dimen/content_padding"
android:paddingRight="@dimen/content_padding"
android:paddingEnd="@dimen/content_padding">
<View
@ -43,4 +45,9 @@
</LinearLayout>
<View
android:id="@+id/space"
android:layout_width="match_parent"
android:layout_height="@dimen/content_padding" />
</LinearLayout>

View file

@ -1088,7 +1088,7 @@
<string name="lang_ja">Японская</string>
<string name="lang_ko">Карэйская</string>
<string name="lang_lv">Латышская</string>
<string name="lang_lt">Летувісская</string>
<string name="lang_lt">Летувіская</string>
<string name="lang_mr">Маратхі</string>
<string name="lang_no">Нарвежская (Bokmål)</string>
<string name="lang_pl">Польская</string>

View file

@ -3843,7 +3843,7 @@ POIの更新は利用できません</string>
<string name="subscription_expired_title">OsmAndLiveサブスクリプションの有効期限が切れました</string>
<string name="subscription_paused_title">OsmAndLiveサブスクリプションが一時停止されました</string>
<string name="subscription_on_hold_title">OsmAndLiveサブスクリプションは保留中です</string>
<string name="markers_history">マーカー履歴</string>
<string name="markers_history">マーカー履歴</string>
<string name="send_files_to_openstreetmap">GPXファイルをOpenStreetMapに送信</string>
<string name="enter_text_separated">タグはカンマで区切って入力してください。</string>
<string name="gpx_upload_public_visibility_descr">\"公開 \"状態は、追跡機能にてユーザーのGPS追跡、公開GPS追跡リスト、および生データのタイムスタンプ付き公開追跡リストに公開されることを意味します。APIを介して提供されるデータはユーザーの追跡ページを参照しません。追跡ポイントのタイムスタンプはパブリックGPS APIでは利用できず、また追跡ポイントは時系列に並んでいません。</string>

View file

@ -3927,4 +3927,5 @@
<string name="poi_cliff_diving">Mergulho de falésia</string>
<string name="poi_zurkhaneh_sport">Zurkhaneh</string>
<string name="poi_bay_filter">Tipo de baía</string>
<string name="poi_ultimate">Ultimate</string>
</resources>

View file

@ -4049,15 +4049,19 @@
<string name="shared_string_route_line">Linha de rota</string>
<string name="route_line_use_map_style_appearance">A linha de rota seria usada %1$s especificado no estilo de mapa selecionado: %2$s.</string>
<string name="specify_color_for_map_mode">Especifique a cor para o modo de mapa: %1$s.</string>
<string name="release_4_0_beta">• As atualizações do OsmAnd Live foram movidas para \"Downloads &gt; Atualizações\"
<string name="release_4_0_beta">"• Adicionada opção para baixar curvas de nível em pés
\n
\n• As trilhas agora podem ser coloridas por altitude, velocidade ou inclinação.
\n • Plano de paisagem da rota: guias adicionadas para alternar entre pontos ou gráficos
\n
\n• Adicionada opção para alterar a aparência da linha da rota de navegação
\n • As atualizações do OsmAnd Live foram movidas para \"Downloads&gt; Atualizações\"
\n
\n• Caixa de diálogo \"Gravação de viagem\" atualizada
\n • As trilhas agora podem ser coloridas por altitude, velocidade ou inclinação.
\n
\n</string>
\n • Adicionada opção para alterar a aparência da linha da rota de navegação
\n
\n • Caixa de diálogo \"Gravação de viagem\" atualizada
\n
\n"</string>
<string name="no_purchases">Você não tem nenhuma compra</string>
<string name="new_device_account">Novo dispositivo / nova conta</string>
<string name="contact_support_description">Se você tiver alguma dúvida, entre em contato conosco em %1$s.</string>
@ -4081,4 +4085,8 @@
<string name="user_points">Pontos do usuário</string>
<string name="output">Saída</string>
<string name="map_quick_action_pattern">%1$s → …</string>
<string name="shared_string_feet">pés</string>
<string name="srtm_unit_format">Formato da unidade de curvas de nível</string>
<string name="srtm_download_list_help_message">OsmAnd fornece dados de linhas de contorno em metros e pés. Você precisará baixar novamente o arquivo para alterar o formato.</string>
<string name="srtm_download_single_help_message">Selecione o formato necessário. Você precisará baixar novamente o arquivo para alterar o formato.</string>
</resources>

View file

@ -193,7 +193,7 @@
<string name="poi_ford_stepping_stones">Ponte pedonal em pedras separadas</string>
<string name="poi_mountain_pass">Passo de montanha</string>
<string name="poi_gate">Portão</string>
<string name="poi_city_wall">Muralha de cidade</string>
<string name="poi_city_wall">Muralha/cerca de cidade</string>
<string name="poi_lift_gate">Cancela elevatória</string>
<string name="poi_toll_booth">Cabine de portagem</string>
<string name="poi_border_control">Controlo aduaneiro</string>
@ -637,13 +637,13 @@
<string name="poi_australian_football">Futebol australiano</string>
<string name="poi_base">Base jumping</string>
<string name="poi_baseball">Beisebol</string>
<string name="poi_basketball">Basquete</string>
<string name="poi_basketball">Basquetebol</string>
<string name="poi_beachvolleyball">Voleibol de praia</string>
<string name="poi_bmx">BMX</string>
<string name="poi_boules">Bocha</string>
<string name="poi_bowls">Lawn bowls</string>
<string name="poi_canadian_football">Futebol canadiano</string>
<string name="poi_canoe">Canoa</string>
<string name="poi_canoe">Canoagem</string>
<string name="poi_chess">Xadrez</string>
<string name="poi_climbing">Escalada</string>
<string name="poi_cricket">Críquete</string>
@ -694,7 +694,7 @@
<string name="poi_boundary_stone">Marco de fronteira</string>
<string name="poi_historic_cannon">Canhão histórico</string>
<string name="poi_castle">Castelo</string>
<string name="poi_city_gate">Portão/arco de cidade</string>
<string name="poi_city_gate">Portão/porta/arco de cidade</string>
<string name="poi_fort">Forte</string>
<string name="poi_fountain">Chafariz</string>
<string name="poi_historic_ruins">Ruínas históricas</string>
@ -707,7 +707,7 @@
<string name="poi_aquarium">Aquário</string>
<string name="poi_theme_park">Parque de diversões</string>
<string name="poi_attraction">Atração turística</string>
<string name="poi_tourism_yes">Objeto turístico</string>
<string name="poi_tourism_yes">Elemento turístico</string>
<string name="poi_attraction_amusement_ride">Atracão de feira</string>
<string name="poi_attraction_animal">Animal (atração)</string>
<string name="poi_attraction_big_wheel">Roda gigante</string>
@ -2866,9 +2866,9 @@
<string name="poi_aquaculture_mussels">Aquicultura: mexilhões</string>
<string name="poi_mdf">Rede de distribuição principal (MDF)</string>
<string name="poi_min_age">Idade mínima</string>
<string name="poi_organic_yes">Sim</string>
<string name="poi_organic_no">Não</string>
<string name="poi_organic_only">Unicamente</string>
<string name="poi_organic_yes">Produtos orgânicos: sim</string>
<string name="poi_organic_no">Produtos orgânicos: não</string>
<string name="poi_organic_only">Produtos orgânicos: unicamente</string>
<string name="poi_traffic_mirror">Espelho de tráfego</string>
<string name="poi_diplomatic_consulate">Consulado</string>
<string name="poi_diplomatic_consulate_general">Consulado geral</string>
@ -3746,7 +3746,7 @@
<string name="poi_community_gender_male">Sexo comunitário: masculino</string>
<string name="poi_community_gender_mixed">Sexo comunitário: misto</string>
<string name="poi_grave">Sepultura</string>
<string name="poi_parking_space">Espaço de estacionamento</string>
<string name="poi_parking_space">Lugar de estacionamento (1 veículo)</string>
<string name="poi_url">URL</string>
<string name="poi_volcano_type">Tipo</string>
<string name="poi_volcano_status">Estado</string>

View file

@ -2013,7 +2013,7 @@
<string name="routing_attr_height_obstacles_name">Utilizar dados de elevação</string>
<string name="rendering_attr_depthContours_description">Mostrar pontos e contornos de profundidade.</string>
<string name="rendering_attr_depthContours_name">Contornos de profundidade náuticos</string>
<string name="show_transparency_seekbar">Mostra a transparência da barra de navegação</string>
<string name="show_transparency_seekbar">Mostrar barra deslizante de transparência</string>
<string name="shared_string_widgets">Widgets</string>
<string name="rendering_attr_hideUnderground_name">Objetos subterrâneos</string>
<string name="auto_split_recording_title">Dividir automaticamente as gravações após quebras</string>

View file

@ -2872,7 +2872,7 @@
<string name="sit_on_the_stop">Посадка на остановке</string>
<string name="use_osm_live_public_transport_description">Включить общественный транспорт с учётом автообновлений OsmAnd Live.</string>
<string name="use_osm_live_public_transport">Общественный транспорт OsmAnd Live</string>
<string name="transfers_size">%1$d пересадки</string>
<string name="transfers_size">пересадки: %1$d</string>
<string name="rendering_attr_surface_unpaved_name">Грунтовая</string>
<string name="rendering_attr_surface_sand_name">Песок</string>
<string name="rendering_attr_surface_grass_name">Трава</string>
@ -3263,7 +3263,7 @@
<string name="selected_profile">Выбранный профиль</string>
<string name="personal_category_name">Персональный</string>
<string name="shared_string_downloading_formatted">Скачивание %s</string>
<string name="rendering_value_thick_name">Толсто</string>
<string name="rendering_value_thick_name">Толстая</string>
<string name="default_speed_dialog_msg">Используется для оценки времени прибытия для неизвестного типа дорог и ограничения скорости для всех дорог (может изменить маршрут)</string>
<string name="routing_attr_allow_intermediate_name">Разрешить промежуточные маршруты</string>
<string name="routing_attr_allow_advanced_name">Разрешить расширенные маршруты</string>
@ -3302,7 +3302,7 @@
<string name="rendering_attr_piste_difficulty_connection_name">Соединение</string>
<string name="simulate_your_location_gpx_descr">Симулировать свою позицию используя записанный GPX трек.</string>
<string name="route_start_point">Начало маршрута</string>
<string name="shared_string_revert">Вернуться</string>
<string name="shared_string_revert">Сброс</string>
<string name="suggested_maps_descr">Эти карты необходимо использовать с плагином.</string>
<string name="added_profiles">Добавленные профили</string>
<string name="added_profiles_descr">Профили, добавленные плагином</string>
@ -4056,7 +4056,11 @@
<string name="next_billing_date">Следующая дата оплаты: %1$s</string>
<string name="osmand_live">OsmAnd Live</string>
<string name="annual_subscription">Годовая подписка</string>
<string name="release_4_0_beta">• Обновления OsmAnd Live перемещены в «Загрузка карт» → «Обновления».
<string name="release_4_0_beta">• Добавлена возможность скачать контурные линии в футах.
\n
\n• Планирование маршрута: добавлены вкладки для переключения между точками и графиками.
\n
\n• Обновления OsmAnd Live перемещены в «Загрузка карт» → «Обновления».
\n
\n• Теперь треки можно раскрашивать по высоте, скорости или уклону.
\n
@ -4087,4 +4091,8 @@
<string name="output">Вывод</string>
<string name="map_quick_action_pattern">%1$s → …</string>
<string name="exit_number">Номер съезда</string>
<string name="srtm_unit_format">Формат единиц на контурных линиях</string>
<string name="srtm_download_single_help_message">Выберите необходимый формат. Для изменения формата потребуется повторно загрузить файл.</string>
<string name="shared_string_feet">футы</string>
<string name="srtm_download_list_help_message">OsmAnd предоставляет данные изолиний в метрах и футах. Вам нужно будет повторно загрузить файл, чтобы изменить формат.</string>
</resources>

View file

@ -4045,7 +4045,11 @@
<string name="lost_data_warning">Všetky neuložené údaje budú stratené.</string>
<string name="show_start_dialog">Zobraziť úvodné okno</string>
<string name="trip_recording_show_start_dialog_setting">Ak je vypnuté, záznam začne hneď po stlačení nástroja alebo položky v menu a preskočí okno nastavenia.</string>
<string name="release_4_0_beta">• Aktualizácie OsmAnd Live presunuté do \"Sťahovania &gt; Aktualizácie\"
<string name="release_4_0_beta">• Pridaná možnosť stiahnutia Vrstevníc v stopách
\n
\n• Plánovať trasy: pridané prepínače medzi bodmi a grafmi
\n
\n• Aktualizácie OsmAnd Live presunuté do \"Sťahovania &gt; Aktualizácie\"
\n
\n • Stopy je teraz možné vyfarbiť podľa nadmorskej výšky, rýchlosti alebo sklonu svahu.
\n
@ -4081,4 +4085,8 @@
<string name="user_points">Body používateľa</string>
<string name="output">Výstup</string>
<string name="map_quick_action_pattern">%1$s → …</string>
<string name="shared_string_feet">stopy</string>
<string name="srtm_unit_format">Formát jednotiek vrstevníc</string>
<string name="srtm_download_list_help_message">OsmAnd poskytuje údaje vrstevníc v metroch a stopách. Budete musieť znovu stiahnuť súbor pre zmenu formátu.</string>
<string name="srtm_download_single_help_message">Prosím zvoľte požadovaný formát. Budete musieť znovu stiahnuť súbor pre zmenu formátu.</string>
</resources>

View file

@ -12,6 +12,9 @@
-->
<string name="shared_string_max_height">Max. height</string>
<string name="shared_string_min_height">Min. height</string>
<string name="route_line_use_gradient_coloring">Route line will be colorized depending on the elevation profile of the route.</string>
<string name="output">Output</string>
<string name="user_points">User points</string>
<string name="announce_when_exceeded">Announce when exceeded</string>

View file

@ -2125,9 +2125,12 @@ public class GpxUiHelper {
public static GPXFile makeGpxFromRoute(RouteCalculationResult route, OsmandApplication app) {
return makeGpxFromLocations(route.getRouteLocations(), app);
}
public static GPXFile makeGpxFromLocations(List<Location> locations, OsmandApplication app) {
double lastHeight = HEIGHT_UNDEFINED;
GPXFile gpx = new GPXUtilities.GPXFile(Version.getFullVersion(app));
List<Location> locations = route.getRouteLocations();
if (locations != null) {
GPXUtilities.Track track = new GPXUtilities.Track();
GPXUtilities.TrkSegment seg = new GPXUtilities.TrkSegment();
@ -2147,6 +2150,8 @@ public class GpxUiHelper {
}
}
lastHeight = h;
} else {
lastHeight = HEIGHT_UNDEFINED;
}
seg.points.add(point);
}

View file

@ -6,12 +6,14 @@ import androidx.annotation.ColorInt;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import net.osmand.plus.track.GradientScaleType;
import net.osmand.util.Algorithms;
public class RouteLineDrawInfo {
private static final String LINE_COLOR_DAY = "line_color_day";
private static final String LINE_COLOR_NIGHT = "line_color_night";
private static final String LINE_COLOR_GRADIENT = "line_color_gradient";
private static final String LINE_WIDTH = "line_width";
private static final String NAVIGATION_ICON_ID = "navigation_icon_id";
private static final String NAVIGATION_ICON_COLOR = "navigation_icon_color";
@ -24,6 +26,7 @@ public class RouteLineDrawInfo {
@ColorInt
private Integer colorDay;
private Integer colorNight;
private GradientScaleType scaleType;
private String width;
// temporally parameters to show in preview
@ -37,9 +40,11 @@ public class RouteLineDrawInfo {
public RouteLineDrawInfo(@Nullable @ColorInt Integer colorDay,
@Nullable @ColorInt Integer colorNight,
@Nullable GradientScaleType gradientScaleType,
@Nullable String width) {
this.colorDay = colorDay;
this.colorNight = colorNight;
this.scaleType = gradientScaleType;
this.width = width;
}
@ -50,6 +55,7 @@ public class RouteLineDrawInfo {
public RouteLineDrawInfo(@NonNull RouteLineDrawInfo existed) {
this.colorDay = existed.colorDay;
this.colorNight = existed.colorNight;
this.scaleType = existed.scaleType;
this.width = existed.width;
this.iconId = existed.iconId;
this.iconColor = existed.iconColor;
@ -71,6 +77,10 @@ public class RouteLineDrawInfo {
this.useDefaultColor = useDefaultColor;
}
public void setGradientScaleType(@Nullable GradientScaleType scaleType) {
this.scaleType = scaleType;
}
public void setWidth(@Nullable String width) {
this.width = width;
}
@ -108,6 +118,11 @@ public class RouteLineDrawInfo {
return nightMode ? colorNight : colorDay;
}
@Nullable
public GradientScaleType getGradientScaleType() {
return scaleType;
}
@Nullable
public String getWidth() {
return width;
@ -141,6 +156,9 @@ public class RouteLineDrawInfo {
if (bundle.containsKey(LINE_COLOR_NIGHT)) {
colorNight = bundle.getInt(LINE_COLOR_NIGHT);
}
if (bundle.containsKey(LINE_COLOR_GRADIENT)) {
scaleType = GradientScaleType.getGradientTypeByName(bundle.getString(LINE_COLOR_GRADIENT));
}
width = bundle.getString(LINE_WIDTH);
iconId = bundle.getInt(NAVIGATION_ICON_ID);
iconColor = bundle.getInt(NAVIGATION_ICON_COLOR);
@ -157,6 +175,9 @@ public class RouteLineDrawInfo {
if (colorNight != null) {
bundle.putInt(LINE_COLOR_NIGHT, colorNight);
}
if (scaleType != null) {
bundle.putString(LINE_COLOR_GRADIENT, scaleType.getTypeName());
}
if (width != null) {
bundle.putString(LINE_WIDTH, width);
}
@ -177,6 +198,7 @@ public class RouteLineDrawInfo {
if (!Algorithms.objectEquals(getColor(false), that.getColor(false))) return false;
if (!Algorithms.objectEquals(getColor(true), that.getColor(true))) return false;
if (!Algorithms.objectEquals(scaleType, that.scaleType)) return false;
return Algorithms.objectEquals(width, that.width);
}
@ -184,6 +206,7 @@ public class RouteLineDrawInfo {
public int hashCode() {
int result = colorDay != null ? colorDay.hashCode() : 0;
result = 31 * result + (colorNight != null ? colorNight.hashCode() : 0);
result = 31 * result + (scaleType != null ? scaleType.getTypeName().hashCode() : 0);
result = 31 * result + (width != null ? width.hashCode() : 0);
return result;
}

View file

@ -21,6 +21,7 @@ import net.osmand.AndroidUtils;
import net.osmand.plus.R;
import net.osmand.plus.UiUtilities;
import net.osmand.plus.activities.MapActivity;
import net.osmand.plus.helpers.AndroidUiHelper;
import net.osmand.plus.helpers.ColorDialogs;
import net.osmand.plus.helpers.enums.DayNightMode;
import net.osmand.plus.routepreparationmenu.cards.BaseCard;
@ -32,6 +33,8 @@ import net.osmand.plus.settings.fragments.HeaderUiAdapter;
import net.osmand.plus.track.AppearanceViewHolder;
import net.osmand.plus.track.ColorsCard;
import net.osmand.plus.track.CustomColorBottomSheet.ColorPickerListener;
import net.osmand.plus.track.GradientCard;
import net.osmand.plus.track.GradientScaleType;
import net.osmand.plus.widgets.multistatetoggle.RadioItem;
import net.osmand.plus.widgets.multistatetoggle.RadioItem.OnRadioItemClickListener;
import net.osmand.plus.widgets.multistatetoggle.TextToggleButton;
@ -51,6 +54,7 @@ public class RouteLineColorCard extends BaseCard implements CardListener, ColorP
private HeaderUiAdapter headerUiAdapter;
private ColorsCard colorsCard;
private GradientCard gradientCard;
private ColorTypeAdapter colorAdapter;
private RecyclerView groupRecyclerView;
private TextView tvDescription;
@ -64,7 +68,9 @@ public class RouteLineColorCard extends BaseCard implements CardListener, ColorP
private enum ColorMode {
DEFAULT(R.string.map_widget_renderer, R.drawable.ic_action_map_style),
CUSTOM(R.string.shared_string_custom, R.drawable.ic_action_settings);
CUSTOM(R.string.shared_string_custom, R.drawable.ic_action_settings),
ALTITUDE(R.string.altitude, R.drawable.ic_action_hillshade_dark),
SLOPE(R.string.shared_string_slope, R.drawable.ic_action_altitude_ascent);
ColorMode(int titleId, int iconId) {
this.titleId = titleId;
@ -108,28 +114,43 @@ public class RouteLineColorCard extends BaseCard implements CardListener, ColorP
setupRadioGroup(radioGroup);
cardsContainer = (ViewGroup) view.findViewById(R.id.colors_card_container);
createColorSelector(cardsContainer);
createCards(cardsContainer);
initSelectedMode();
}
private void initSelectedMode() {
selectedMode = getRouteLineColor() == null ? ColorMode.DEFAULT : ColorMode.CUSTOM;
if (routeLineDrawInfo.getGradientScaleType() == GradientScaleType.ALTITUDE) {
selectedMode = ColorMode.ALTITUDE;
} else if (routeLineDrawInfo.getGradientScaleType() == GradientScaleType.SLOPE) {
selectedMode = ColorMode.SLOPE;
} else {
selectedMode = getRouteLineColor() == null ? ColorMode.DEFAULT : ColorMode.CUSTOM;
}
modeChanged();
}
private void modeChanged() {
if (selectedMode == ColorMode.DEFAULT) {
themeToggleContainer.setVisibility(View.GONE);
cardsContainer.setVisibility(View.GONE);
AndroidUiHelper.updateVisibility(colorsCard.getView(), false);
AndroidUiHelper.updateVisibility(gradientCard.getView(), false);
routeLineDrawInfo.setUseDefaultColor(true);
changeMapTheme(initMapTheme);
} else {
} else if (selectedMode == ColorMode.CUSTOM) {
themeToggleContainer.setVisibility(View.VISIBLE);
cardsContainer.setVisibility(View.VISIBLE);
AndroidUiHelper.updateVisibility(colorsCard.getView(), true);
AndroidUiHelper.updateVisibility(gradientCard.getView(), false);
routeLineDrawInfo.setUseDefaultColor(false);
changeMapTheme(isNightMap() ? DayNightMode.NIGHT : DayNightMode.DAY);
} else {
gradientCard.setSelectedScaleType(getGradientScaleTypeFromMode());
AndroidUiHelper.updateVisibility(colorsCard.getView(), false);
AndroidUiHelper.updateVisibility(gradientCard.getView(), true);
themeToggleContainer.setVisibility(View.GONE);
routeLineDrawInfo.setUseDefaultColor(false);
}
routeLineDrawInfo.setGradientScaleType(getGradientScaleTypeFromMode());
updateColorItems();
updateDescription();
}
@ -170,7 +191,7 @@ public class RouteLineColorCard extends BaseCard implements CardListener, ColorP
}
}
private void createColorSelector(ViewGroup container) {
private void createCards(ViewGroup container) {
MapActivity mapActivity = getMapActivity();
if (mapActivity != null) {
List<Integer> colors = new ArrayList<>();
@ -184,6 +205,9 @@ public class RouteLineColorCard extends BaseCard implements CardListener, ColorP
colorsCard = new ColorsCard(mapActivity, selectedColor, targetFragment, colors, preference, null);
colorsCard.setListener(this);
container.addView(colorsCard.build(mapActivity));
gradientCard = new GradientCard(mapActivity, routeLineDrawInfo.getGradientScaleType());
container.addView(gradientCard.build(mapActivity));
}
}
@ -212,6 +236,17 @@ public class RouteLineColorCard extends BaseCard implements CardListener, ColorP
return routeLineDrawInfo.getColor(isNightMap());
}
@Nullable
private GradientScaleType getGradientScaleTypeFromMode() {
if (selectedMode == ColorMode.ALTITUDE) {
return GradientScaleType.ALTITUDE;
} else if (selectedMode == ColorMode.SLOPE) {
return GradientScaleType.SLOPE;
} else {
return null;
}
}
private void updateSelectedColor() {
int selectedColor = colorsCard.getSelectedColor();
routeLineDrawInfo.setColor(selectedColor, isNightMap());
@ -235,6 +270,8 @@ public class RouteLineColorCard extends BaseCard implements CardListener, ColorP
String colorName = "";
if (selectedMode == ColorMode.DEFAULT) {
colorName = app.getString(R.string.map_widget_renderer);
} else if (selectedMode == ColorMode.ALTITUDE || selectedMode == ColorMode.SLOPE) {
colorName = app.getString(selectedMode.titleId);
} else if (getRouteLineColor() != null) {
int colorNameId = ColorDialogs.getColorName(getRouteLineColor());
colorName = app.getString(colorNameId);
@ -248,10 +285,12 @@ public class RouteLineColorCard extends BaseCard implements CardListener, ColorP
String pattern = app.getString(R.string.route_line_use_map_style_appearance);
String color = app.getString(R.string.shared_string_color).toLowerCase();
description = String.format(pattern, color, app.getRendererRegistry().getSelectedRendererName());
} else {
} else if (selectedMode == ColorMode.CUSTOM) {
String pattern = app.getString(R.string.specify_color_for_map_mode);
String mapModeTitle = app.getString(isNightMap() ? NIGHT_TITLE_ID : DAY_TITLE_ID);
description = String.format(pattern, mapModeTitle.toLowerCase());
} else {
description = app.getString(R.string.route_line_use_gradient_coloring);
}
tvDescription.setText(description);
}
@ -368,4 +407,4 @@ public class RouteLineColorCard extends BaseCard implements CardListener, ColorP
void onMapThemeUpdated(@NonNull DayNightMode mapTheme);
}
}
}

View file

@ -2708,6 +2708,7 @@ public class OsmandSettings {
public final ListStringPreference CUSTOM_ROUTE_LINE_COLORS = (ListStringPreference) new ListStringPreference(this, "custom_route_line_colors", null, ",").makeShared().makeGlobal();
public final CommonPreference<Integer> ROUTE_LINE_COLOR_DAY = new IntPreference(this, "route_line_color", 0).cache().makeProfile();
public final CommonPreference<Integer> ROUTE_LINE_COLOR_NIGHT = new IntPreference(this, "route_line_color_night", 0).cache().makeProfile();
public final CommonPreference<GradientScaleType> ROUTE_LINE_GRADIENT = new EnumStringPreference<>(this, "route_line_gradient", null, new GradientScaleType[] {GradientScaleType.ALTITUDE, GradientScaleType.SLOPE}).cache().makeProfile();
public final CommonPreference<String> ROUTE_LINE_WIDTH = new StringPreference(this, "route_line_width", null).makeProfile();
public final OsmandPreference<Boolean> USE_OSM_LIVE_FOR_ROUTING = new BooleanPreference(this, "enable_osmc_routing", true).makeProfile();

View file

@ -63,6 +63,7 @@ import net.osmand.plus.settings.backend.backup.SettingsHelper;
import net.osmand.plus.settings.fragments.RouteLineAppearanceFragment.OnApplyRouteLineListener;
import net.osmand.plus.track.ColorsCard;
import net.osmand.plus.track.CustomColorBottomSheet.ColorPickerListener;
import net.osmand.plus.track.GradientScaleType;
import net.osmand.plus.widgets.FlowLayout;
import net.osmand.plus.widgets.OsmandTextFieldBoxes;
import net.osmand.util.Algorithms;
@ -1021,8 +1022,9 @@ public class ProfileAppearanceFragment extends BaseSettingsFragment implements O
private RouteLineDrawInfo createRouteLineDrawInfo(@NonNull ApplicationMode appMode) {
Integer colorDay = getRouteLineColor(appMode, settings.ROUTE_LINE_COLOR_DAY);
Integer colorNight = getRouteLineColor(appMode, settings.ROUTE_LINE_COLOR_NIGHT);
GradientScaleType scaleType = settings.ROUTE_LINE_GRADIENT.getModeValue(appMode);
String widthKey = settings.ROUTE_LINE_WIDTH.getModeValue(appMode);
return new RouteLineDrawInfo(colorDay, colorNight, widthKey);
return new RouteLineDrawInfo(colorDay, colorNight, scaleType, widthKey);
}
private Integer getRouteLineColor(@NonNull ApplicationMode appMode,
@ -1035,6 +1037,7 @@ public class ProfileAppearanceFragment extends BaseSettingsFragment implements O
@NonNull RouteLineDrawInfo drawInfo) {
saveRouteLineColor(appMode, settings.ROUTE_LINE_COLOR_DAY, drawInfo.getColor(false));
saveRouteLineColor(appMode, settings.ROUTE_LINE_COLOR_NIGHT, drawInfo.getColor(true));
settings.ROUTE_LINE_GRADIENT.setModeValue(appMode, drawInfo.getGradientScaleType());
settings.ROUTE_LINE_WIDTH.setModeValue(appMode, drawInfo.getWidth());
}

View file

@ -19,15 +19,24 @@ import androidx.annotation.Nullable;
public class GradientCard extends BaseCard {
private GPXTrackAnalysis gpxTrackAnalysis;
private final GPXTrackAnalysis gpxTrackAnalysis;
private GradientScaleType selectedScaleType;
private final int minSlope = 0;
private final int maxSlope = 60;
public GradientCard(@NonNull MapActivity mapActivity, @NonNull GPXTrackAnalysis gpxTrackAnalysis, @Nullable GradientScaleType selectedScaleType) {
super(mapActivity);
this.gpxTrackAnalysis = gpxTrackAnalysis;
this.selectedScaleType = selectedScaleType;
}
public GradientCard(@NonNull MapActivity mapActivity, @Nullable GradientScaleType scaleType) {
super(mapActivity);
this.gpxTrackAnalysis = null;
this.selectedScaleType = scaleType;
}
@Override
public int getCardLayoutId() {
return R.layout.gradient_card;
@ -40,14 +49,27 @@ public class GradientCard extends BaseCard {
return;
}
AndroidUiHelper.updateVisibility(view, true);
TextView minValue = view.findViewById(R.id.min_value);
TextView maxValue = view.findViewById(R.id.max_value);
double min = RouteColorize.getMinValue(selectedScaleType.toColorizationType(), gpxTrackAnalysis);
double max = RouteColorize.getMaxValue(selectedScaleType.toColorizationType(),
gpxTrackAnalysis, min, app.getSettings().getApplicationMode().getMaxSpeed());
minValue.setText(formatValue(min));
maxValue.setText(formatValue(max));
if (gpxTrackAnalysis != null) {
AndroidUiHelper.updateVisibility(view, true);
double min = RouteColorize.getMinValue(selectedScaleType.toColorizationType(), gpxTrackAnalysis);
double max = RouteColorize.getMaxValue(selectedScaleType.toColorizationType(),
gpxTrackAnalysis, min, app.getSettings().getApplicationMode().getMaxSpeed());
minValue.setText(formatValue(min));
maxValue.setText(formatValue(max));
AndroidUiHelper.updateVisibility(view.findViewById(R.id.space), true);
} else {
if (selectedScaleType == GradientScaleType.ALTITUDE) {
minValue.setText(R.string.shared_string_min_height);
maxValue.setText(R.string.shared_string_max_height);
} else if (selectedScaleType == GradientScaleType.SLOPE) {
minValue.setText(formatValue(minSlope));
maxValue.setText(formatValue(maxSlope));
}
AndroidUiHelper.updateVisibility(view.findViewById(R.id.space), false);
}
}
public void setSelectedScaleType(GradientScaleType type) {
@ -59,7 +81,7 @@ public class GradientCard extends BaseCard {
if (selectedScaleType == GradientScaleType.ALTITUDE) {
return OsmAndFormatter.getFormattedAlt(value, app);
} else if (selectedScaleType == GradientScaleType.SLOPE) {
return (int) value + " %";
return app.getString(R.string.ltr_or_rtl_combine_via_space, String.valueOf((int) value), "%");
}
String speed = OsmAndFormatter.getFormattedSpeed((float) value, app);
String speedUnit = app.getSettings().SPEED_SYSTEM.get().toShortString(app);

View file

@ -4,6 +4,7 @@ import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.LinearGradient;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Paint.Cap;
@ -11,6 +12,7 @@ import android.graphics.Path;
import android.graphics.PointF;
import android.graphics.PorterDuff.Mode;
import android.graphics.PorterDuffColorFilter;
import android.graphics.Shader;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.LayerDrawable;
@ -44,6 +46,7 @@ import net.osmand.plus.routing.RouteService;
import net.osmand.plus.routing.RoutingHelper;
import net.osmand.plus.routing.TransportRoutingHelper;
import net.osmand.plus.settings.backend.CommonPreference;
import net.osmand.plus.track.GradientScaleType;
import net.osmand.plus.views.OsmandMapLayer;
import net.osmand.plus.views.OsmandMapTileView;
import net.osmand.plus.views.layers.ContextMenuLayer.IContextMenuProvider;
@ -54,6 +57,7 @@ import net.osmand.plus.views.layers.geometry.RouteGeometryWayContext;
import net.osmand.render.RenderingRuleProperty;
import net.osmand.render.RenderingRuleSearchRequest;
import net.osmand.render.RenderingRulesStorage;
import net.osmand.router.RouteColorize;
import net.osmand.router.TransportRouteResult;
import net.osmand.util.Algorithms;
import net.osmand.util.MapUtils;
@ -109,6 +113,7 @@ public class RouteLayer extends OsmandMapLayer implements IContextMenuProvider {
private int routeLineColor;
private Integer directionArrowsColor;
private GradientScaleType gradientScaleType = null;
public RouteLayer(RoutingHelper helper) {
this.helper = helper;
@ -136,6 +141,8 @@ public class RouteLayer extends OsmandMapLayer implements IContextMenuProvider {
attrs.defaultWidth = (int) (12 * density);
attrs.defaultWidth3 = (int) (7 * density);
attrs.defaultColor = view.getResources().getColor(R.color.nav_track);
attrs.shadowPaint.setColor(0x80000000);
attrs.shadowPaint.setStrokeCap(Cap.ROUND);
attrs.paint3.setStrokeCap(Cap.BUTT);
attrs.paint3.setColor(Color.WHITE);
attrs.paint2.setStrokeCap(Cap.BUTT);
@ -331,14 +338,24 @@ public class RouteLayer extends OsmandMapLayer implements IContextMenuProvider {
DrawSettings settings,
RouteLineDrawInfo drawInfo) {
updateAttrs(settings, tileBox);
updateRouteColors(nightMode);
paintRouteLinePreview.setColor(getRouteLineColor());
paintRouteLinePreview.setStrokeWidth(getRouteLineWidth(tileBox));
int centerX = drawInfo.getCenterX();
int centerY = drawInfo.getCenterY();
int screenHeight = drawInfo.getScreenHeight();
updateRouteColors(nightMode);
updateRouteGradient();
LinearGradient gradient = null;
if (gradientScaleType == GradientScaleType.ALTITUDE || gradientScaleType == GradientScaleType.SLOPE) {
int[] colors = new int[] {RouteColorize.RED, RouteColorize.YELLOW, RouteColorize.GREEN};
float[] positions = new float[] {0, 0.5f, 1};
gradient = new LinearGradient(centerX, 0, centerX, screenHeight, colors, positions, Shader.TileMode.CLAMP);
}
paintRouteLinePreview.setShader(gradient);
paintRouteLinePreview.setColor(getRouteLineColor());
canvas.drawLine(centerX, 0, centerX, screenHeight, paintRouteLinePreview);
if (previewIcon == null) {
@ -460,6 +477,14 @@ public class RouteLayer extends OsmandMapLayer implements IContextMenuProvider {
routeLineColor = color;
}
private void updateRouteGradient() {
if (routeLineDrawInfo != null) {
gradientScaleType = routeLineDrawInfo.getGradientScaleType();
} else {
gradientScaleType = view.getSettings().ROUTE_LINE_GRADIENT.getModeValue(helper.getAppMode());
}
}
private float getRouteLineWidth(@NonNull RotatedTileBox tileBox) {
String widthKey;
if (routeLineDrawInfo != null) {
@ -524,9 +549,10 @@ public class RouteLayer extends OsmandMapLayer implements IContextMenuProvider {
boolean directTo = route.getRouteService() == RouteService.DIRECT_TO;
boolean straight = route.getRouteService() == RouteService.STRAIGHT;
publicTransportRouteGeometry.clearRoute();
routeGeometry.updateRoute(tb, route);
updateRouteColors(nightMode);
routeGeometry.setRouteStyleParams(getRouteLineColor(), getRouteLineWidth(tb), getDirectionArrowsColor());
updateRouteGradient();
routeGeometry.setRouteStyleParams(getRouteLineColor(), getRouteLineWidth(tb), getDirectionArrowsColor(), gradientScaleType);
routeGeometry.updateRoute(tb, route, view.getApplication());
if (directTo) {
routeGeometry.drawSegments(tb, canvas, topLatitude, leftLongitude, bottomLatitude, rightLongitude,
null, 0);

View file

@ -126,7 +126,7 @@ public abstract class GeometryWay<T extends GeometryWayContext, D extends Geomet
zooms.clear();
}
private PathGeometryZoom getGeometryZoom(RotatedTileBox tb) {
protected PathGeometryZoom getGeometryZoom(RotatedTileBox tb, Map<Integer, PathGeometryZoom> zooms) {
int zoom = tb.getZoom();
PathGeometryZoom zm = zooms.size() > zoom ? zooms.get(zoom) : null;
if (zm == null) {
@ -139,6 +139,11 @@ public abstract class GeometryWay<T extends GeometryWayContext, D extends Geomet
@NonNull
public abstract GeometryWayStyle<?> getDefaultWayStyle();
@NonNull
protected Map<Integer, GeometryWayStyle<?>> getStyleMap() {
return styleMap;
}
public Location getNextVisiblePoint() {
return null;
}
@ -148,7 +153,7 @@ public abstract class GeometryWay<T extends GeometryWayContext, D extends Geomet
if (locationProvider == null || locationProvider.getSize() == 0) {
return;
}
PathGeometryZoom geometryZoom = getGeometryZoom(tb);
PathGeometryZoom geometryZoom = getGeometryZoom(tb, zooms);
TByteArrayList simplification = geometryZoom.getSimplifyPoints();
List<Double> odistances = geometryZoom.getDistances();
@ -168,32 +173,27 @@ public abstract class GeometryWay<T extends GeometryWayContext, D extends Geomet
int previous = -1;
for (int i = startLocationIndex; i < locationProvider.getSize(); i++) {
style = getStyle(i, defaultWayStyle);
if (simplification.getQuick(i) == 0 && !styleMap.containsKey(i)) {
if (shouldSkipLocation(simplification, styleMap, i)) {
continue;
}
double lat = locationProvider.getLatitude(i);
double lon = locationProvider.getLongitude(i);
if (shouldAddLocation(simplification, leftLongitude, rightLongitude, bottomLatitude, topLatitude,
locationProvider, i)) {
double dist = previous == -1 ? 0 : odistances.get(i);
if (!previousVisible) {
double prevLat = Double.NaN;
double prevLon = Double.NaN;
if (previous != -1) {
prevLat = locationProvider.getLatitude(previous);
prevLon = locationProvider.getLongitude(previous);
addLocation(tb, previous, style, tx, ty, angles, distances, dist, styles);
} else if (lastProjection != null) {
prevLat = lastProjection.getLatitude();
prevLon = lastProjection.getLongitude();
}
if (!Double.isNaN(prevLat) && !Double.isNaN(prevLon)) {
addLocation(tb, prevLat, prevLon, getStyle(i - 1, style), tx, ty, angles, distances, dist, styles); // first point
double prevLat = lastProjection.getLatitude();
double prevLon = lastProjection.getLongitude();
if (!Double.isNaN(prevLat) && !Double.isNaN(prevLon)) {
addLocation(tb, prevLat, prevLon, getStyle(i - 1, style), tx, ty, angles, distances, dist, styles); // first point
}
}
}
addLocation(tb, lat, lon, style, tx, ty, angles, distances, dist, styles);
addLocation(tb, i, style, tx, ty, angles, distances, dist, styles);
previousVisible = true;
} else if (previousVisible) {
addLocation(tb, lat, lon, style, tx, ty, angles, distances, previous == -1 ? 0 : odistances.get(i), styles);
addLocation(tb, i, style, tx, ty, angles, distances, previous == -1 ? 0 : odistances.get(i), styles);
double distToFinish = 0;
for (int ki = i + 1; ki < odistances.size(); ki++) {
distToFinish += odistances.get(ki);
@ -207,6 +207,10 @@ public abstract class GeometryWay<T extends GeometryWayContext, D extends Geomet
drawRouteSegment(tb, canvas, tx, ty, angles, distances, 0, styles);
}
protected boolean shouldSkipLocation(TByteArrayList simplification, Map<Integer, GeometryWayStyle<?>> styleMap, int locationIdx) {
return simplification.getQuick(locationIdx) == 0 && !styleMap.containsKey(locationIdx);
}
protected boolean shouldAddLocation(TByteArrayList simplification, double leftLon, double rightLon, double bottomLat,
double topLat, GeometryWayProvider provider, int currLocationIdx) {
double lat = provider.getLatitude(currLocationIdx);
@ -214,7 +218,14 @@ public abstract class GeometryWay<T extends GeometryWayContext, D extends Geomet
return leftLon <= lon && lon <= rightLon && bottomLat <= lat && lat <= topLat;
}
private void addLocation(RotatedTileBox tb, double latitude, double longitude, GeometryWayStyle<?> style,
protected void addLocation(RotatedTileBox tb, int locationIdx, GeometryWayStyle<?> style,
List<Float> tx, List<Float> ty, List<Double> angles, List<Double> distances,
double dist, List<GeometryWayStyle<?>> styles) {
addLocation(tb, locationProvider.getLatitude(locationIdx), locationProvider.getLongitude(locationIdx),
style, tx, ty, angles, distances, dist, styles);
}
protected void addLocation(RotatedTileBox tb, double latitude, double longitude, GeometryWayStyle<?> style,
List<Float> tx, List<Float> ty, List<Double> angles, List<Double> distances,
double dist, List<GeometryWayStyle<?>> styles) {
float x = tb.getPixXFromLatLon(latitude, longitude);
@ -356,13 +367,26 @@ public abstract class GeometryWay<T extends GeometryWayContext, D extends Geomet
List<Pair<Path, GeometryWayStyle<?>>> paths = new ArrayList<>();
canvas.rotate(-tb.getRotate(), tb.getCenterPixelX(), tb.getCenterPixelY());
calculatePath(tb, tx, ty, styles, paths);
for (Pair<Path, GeometryWayStyle<?>> pc : paths) {
drawer.drawFullBorder(canvas, tb.getZoom(), paths);
drawer.drawSegmentBorder(canvas, tb.getZoom(), paths.get(0).first, paths.get(0).second);
for (int i = 1; i <= paths.size(); i++) {
if (i != paths.size()) {
Pair<Path, GeometryWayStyle<?>> prev = paths.get(i);
if (prev.second.hasPathLine()) {
drawer.drawSegmentBorder(canvas, tb.getZoom(), prev.first, prev.second);
}
}
Pair<Path, GeometryWayStyle<?>> pc = paths.get(i - 1);
GeometryWayStyle<?> style = pc.second;
if (style.hasPathLine()) {
drawer.drawPath(canvas, pc.first, style);
}
}
context.clearCustomColor();
context.clearCustomShader();
}
drawer.drawArrowsOverPath(canvas, tb, tx, ty, angles, distances, distToFinish, styles);
} finally {
@ -372,7 +396,7 @@ public abstract class GeometryWay<T extends GeometryWayContext, D extends Geomet
}
}
private static class PathGeometryZoom {
protected static class PathGeometryZoom {
private static final float EPSILON_IN_DPI = 2;
@ -390,12 +414,7 @@ public abstract class GeometryWay<T extends GeometryWayContext, D extends Geomet
angles = new ArrayList<>(size);
if (simplify) {
simplifyPoints.fill(0, size, (byte) 0);
if (size > 0) {
simplifyPoints.set(0, (byte) 1);
}
double distInPix = (tb.getDistance(0, 0, tb.getPixWidth(), 0) / tb.getPixWidth());
double cullDistance = (distInPix * (EPSILON_IN_DPI * Math.max(1, tb.getDensity())));
cullRamerDouglasPeucker(simplifyPoints, locationProvider, 0, size - 1, cullDistance);
simplify(tb, locationProvider, simplifyPoints);
} else {
simplifyPoints.fill(0, size, (byte) 1);
}
@ -422,6 +441,16 @@ public abstract class GeometryWay<T extends GeometryWayContext, D extends Geomet
}
}
protected void simplify(RotatedTileBox tb, GeometryWayProvider locationProvider, TByteArrayList simplifyPoints) {
int size = locationProvider.getSize();
if (size > 0) {
simplifyPoints.set(0, (byte) 1);
}
double distInPix = (tb.getDistance(0, 0, tb.getPixWidth(), 0) / tb.getPixWidth());
double cullDistance = (distInPix * (EPSILON_IN_DPI * Math.max(1, tb.getDensity())));
cullRamerDouglasPeucker(simplifyPoints, locationProvider, 0, size - 1, cullDistance);
}
public List<Double> getDistances() {
return distances;
}

View file

@ -105,6 +105,10 @@ public abstract class GeometryWayContext {
attrs.customColor = 0;
}
public void clearCustomShader() {
attrs.customColorPaint.setShader(null);
}
public int getStrokeColor(int sourceColor) {
return ColorUtils.blendARGB(sourceColor, Color.BLACK, 0.6f);
}

View file

@ -7,6 +7,7 @@ import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffColorFilter;
import android.util.Pair;
import net.osmand.data.RotatedTileBox;
@ -89,6 +90,12 @@ public class GeometryWayDrawer<T extends GeometryWayContext> {
}
}
protected void drawFullBorder(Canvas canvas, int zoom, List<Pair<Path, GeometryWayStyle<?>>> paths) {
}
protected void drawSegmentBorder(Canvas canvas, int zoom, Path path, GeometryWayStyle<?> style) {
}
protected PathPoint getArrowPathPoint(float iconx, float icony, GeometryWayStyle<?> style, double angle) {
return new PathPoint(iconx, icony, angle, style);
}

View file

@ -2,20 +2,32 @@ package net.osmand.plus.views.layers.geometry;
import android.graphics.Bitmap;
import android.graphics.Paint;
import android.graphics.PointF;
import net.osmand.GPXUtilities;
import net.osmand.GPXUtilities.GPXFile;
import net.osmand.Location;
import net.osmand.data.RotatedTileBox;
import net.osmand.plus.OsmandApplication;
import net.osmand.plus.helpers.GpxUiHelper;
import net.osmand.plus.routing.RouteCalculationResult;
import net.osmand.plus.routing.RoutingHelper;
import net.osmand.plus.track.GradientScaleType;
import net.osmand.router.RouteColorize;
import net.osmand.router.RouteColorize.RouteColorizationPoint;
import net.osmand.util.Algorithms;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import androidx.annotation.ColorInt;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import gnu.trove.list.array.TByteArrayList;
import net.osmand.Location;
import net.osmand.data.RotatedTileBox;
import net.osmand.plus.routing.RouteCalculationResult;
import net.osmand.plus.routing.RoutingHelper;
import java.util.Collections;
import java.util.List;
public class RouteGeometryWay extends GeometryWay<RouteGeometryWayContext, GeometryWayDrawer<RouteGeometryWayContext>> {
public class RouteGeometryWay extends GeometryWay<RouteGeometryWayContext, RouteGeometryWayDrawer> {
private RoutingHelper helper;
private RouteCalculationResult route;
@ -23,34 +35,101 @@ public class RouteGeometryWay extends GeometryWay<RouteGeometryWayContext, Geome
private Integer customColor;
private Float customWidth;
private Integer customPointColor;
private GradientScaleType scaleType;
private GradientScaleType prevScaleType;
public RouteGeometryWay(RouteGeometryWayContext context) {
super(context, new GeometryWayDrawer<>(context));
super(context, new RouteGeometryWayDrawer(context, true));
this.helper = context.getApp().getRoutingHelper();
}
public void setRouteStyleParams(@Nullable @ColorInt Integer color,
@Nullable Float width,
@Nullable @ColorInt Integer pointColor) {
@Nullable @ColorInt Integer pointColor,
@Nullable GradientScaleType scaleType) {
if (scaleType != null && !Algorithms.objectEquals(customWidth, width)) {
for (GeometryWayStyle<?> style : getStyleMap().values()) {
style.width = width;
}
}
this.customColor = color;
this.customWidth = width;
this.customPointColor = pointColor;
this.prevScaleType = this.scaleType;
this.scaleType = scaleType;
if (width != null) {
getContext().getAttrs().shadowPaint.setStrokeWidth(width + getContext().getDensity() * 2);
}
getContext().getAttrs().customColorPaint.setStrokeCap(scaleType != null ? Paint.Cap.ROUND : Paint.Cap.BUTT);
}
@NonNull
@Override
public GeometryWayStyle<RouteGeometryWayContext> getDefaultWayStyle() {
Paint paint = getContext().getAttrs().paint;
int color = customColor != null ? customColor : paint.getColor();
float width = customWidth != null ? customWidth : paint.getStrokeWidth();
return new GeometrySolidWayStyle(getContext(), color, width, customPointColor);
}
public void updateRoute(RotatedTileBox tb, RouteCalculationResult route) {
if (tb.getMapDensity() != getMapDensity() || this.route != route) {
public void updateRoute(RotatedTileBox tb, RouteCalculationResult route, OsmandApplication app) {
if (tb.getMapDensity() != getMapDensity() || this.route != route || prevScaleType != scaleType) {
this.route = route;
List<Location> locations = route != null ? route.getImmutableAllLocations() : Collections.<Location>emptyList();
updateWay(locations, tb);
if (scaleType == null || locations.size() < 2) {
updateWay(locations, tb);
return;
}
GPXFile gpxFile = GpxUiHelper.makeGpxFromLocations(locations, app);
if (!gpxFile.hasAltitude) {
updateWay(locations, tb);
return;
}
// Start point can have wrong zero altitude
List<GPXUtilities.WptPt> pts = gpxFile.tracks.get(0).segments.get(0).points;
GPXUtilities.WptPt firstPt = pts.get(0);
if (firstPt.ele == 0) {
firstPt.ele = pts.get(1).ele;
}
RouteColorize routeColorize = new RouteColorize(tb.getZoom(), gpxFile, null, scaleType.toColorizationType(), 0);
List<RouteColorizationPoint> points = routeColorize.getResult(false);
updateWay(new GradientGeometryWayProvider(routeColorize, points), createStyles(points), tb);
}
}
private Map<Integer, GeometryWayStyle<?>> createStyles(List<RouteColorizationPoint> points) {
Map<Integer, GeometryWayStyle<?>> styleMap = new TreeMap<>();
for (int i = 1; i < points.size(); i++) {
GeometryGradientWayStyle style = getGradientWayStyle();
style.startColor = points.get(i - 1).color;
style.endColor = points.get(i).color;
styleMap.put(i, style);
}
return styleMap;
}
@Override
protected boolean shouldSkipLocation(TByteArrayList simplification, Map<Integer, GeometryWayStyle<?>> styleMap, int locationIdx) {
return scaleType == null ?
super.shouldSkipLocation(simplification, styleMap, locationIdx) :
simplification.getQuick(locationIdx) == 0;
}
@Override
protected void addLocation(RotatedTileBox tb, int locationIdx, GeometryWayStyle<?> style, List<Float> tx, List<Float> ty, List<Double> angles, List<Double> distances, double dist, List<GeometryWayStyle<?>> styles) {
super.addLocation(tb, getLocationProvider().getLatitude(locationIdx),
getLocationProvider().getLongitude(locationIdx), style, tx, ty, angles, distances, dist, styles);
if (scaleType != null && tx.size() - 1 > 0) {
int idx = tx.size() - 1;
((GeometryGradientWayStyle) style).startXY = new PointF(tx.get(idx - 1), ty.get(idx - 1));
((GeometryGradientWayStyle) style).endXY = new PointF(tx.get(idx), ty.get(idx));
}
}
@Override
protected void addLocation(RotatedTileBox tb, double latitude, double longitude, GeometryWayStyle<?> style, List<Float> tx, List<Float> ty, List<Double> angles, List<Double> distances, double dist, List<GeometryWayStyle<?>> styles) {
super.addLocation(tb, latitude, longitude, style, tx, ty, angles, distances, dist, styles);
if (scaleType != null) {
int lastIdx = tx.size() - 1;
((GeometryGradientWayStyle) style).startXY = new PointF(tx.get(lastIdx), ty.get(lastIdx));
((GeometryGradientWayStyle) style).endXY = new PointF(tx.get(lastIdx), ty.get(lastIdx));
((GeometryGradientWayStyle) style).startColor = getGradientLocationProvider().getColor(0);
((GeometryGradientWayStyle) style).endColor = getGradientLocationProvider().getColor(0);
}
}
@ -61,17 +140,125 @@ public class RouteGeometryWay extends GeometryWay<RouteGeometryWayContext, Geome
}
}
@NonNull
@Override
public GeometryWayStyle<RouteGeometryWayContext> getDefaultWayStyle() {
Paint paint = getContext().getAttrs().paint;
int color = customColor != null ? customColor : paint.getColor();
float width = customWidth != null ? customWidth : paint.getStrokeWidth();
return scaleType == null ?
new GeometrySolidWayStyle(getContext(), color, width, customPointColor) :
new GeometryGradientWayStyle(getContext(), color, width);
}
private GeometryGradientWayStyle getGradientWayStyle() {
return (GeometryGradientWayStyle) getDefaultWayStyle();
}
@Override
public Location getNextVisiblePoint() {
return helper.getRoute().getCurrentStraightAnglePoint();
}
private GradientGeometryWayProvider getGradientLocationProvider() {
return (GradientGeometryWayProvider) getLocationProvider();
}
@Override
protected PathGeometryZoom getGeometryZoom(RotatedTileBox tb, Map<Integer, PathGeometryZoom> zooms) {
if (scaleType == null) {
return super.getGeometryZoom(tb, zooms);
}
int zoom = tb.getZoom();
PathGeometryZoom zm = zooms.get(zoom);
if (zm == null) {
zm = new GradientPathGeometryZoom(getLocationProvider(), tb, true);
zooms.put(zoom, zm);
}
return zm;
}
private static class GradientGeometryWayProvider implements GeometryWayProvider {
private final RouteColorize routeColorize;
private final List<RouteColorizationPoint> locations;
public GradientGeometryWayProvider(RouteColorize routeColorize, List<RouteColorizationPoint> locations) {
this.routeColorize = routeColorize;
this.locations = locations;
}
public List<RouteColorizationPoint> simplify(int zoom) {
return routeColorize.simplify(zoom);
}
public int getColor(int index) {
return locations.get(index).color;
}
@Override
public double getLatitude(int index) {
return locations.get(index).lat;
}
@Override
public double getLongitude(int index) {
return locations.get(index).lon;
}
@Override
public int getSize() {
return locations.size();
}
}
private static class GradientPathGeometryZoom extends PathGeometryZoom {
public GradientPathGeometryZoom(GeometryWayProvider locationProvider, RotatedTileBox tb, boolean simplify) {
super(locationProvider, tb, simplify);
}
@Override
protected void simplify(RotatedTileBox tb, GeometryWayProvider locationProvider, TByteArrayList simplifyPoints) {
if (locationProvider instanceof GradientGeometryWayProvider) {
GradientGeometryWayProvider provider = (GradientGeometryWayProvider) locationProvider;
List<RouteColorizationPoint> simplified = provider.simplify(tb.getZoom());
for (RouteColorizationPoint location : simplified) {
simplifyPoints.set(location.id, (byte) 1);
}
}
}
}
public static class GeometryGradientWayStyle extends GeometryWayStyle<RouteGeometryWayContext> {
public int startColor;
public int endColor;
public PointF startXY;
public PointF endXY;
public GeometryGradientWayStyle(RouteGeometryWayContext context, Integer color, Float width) {
super(context, color, width);
}
@Override
public Bitmap getPointBitmap() {
return getContext().getArrowBitmap();
}
@Override
public boolean equals(Object other) {
return this == other;
}
}
private static class GeometrySolidWayStyle extends GeometryWayStyle<RouteGeometryWayContext> {
private Integer pointColor;
GeometrySolidWayStyle(RouteGeometryWayContext context, Integer color, Float width,
Integer pointColor) {
Integer pointColor) {
super(context, color, width);
this.pointColor = pointColor;
}
@ -97,4 +284,4 @@ public class RouteGeometryWay extends GeometryWay<RouteGeometryWayContext, Geome
return other instanceof GeometrySolidWayStyle;
}
}
}
}

View file

@ -0,0 +1,57 @@
package net.osmand.plus.views.layers.geometry;
import android.graphics.Canvas;
import android.graphics.LinearGradient;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.Shader;
import android.util.Pair;
import net.osmand.plus.views.MapTileLayer;
import net.osmand.plus.views.layers.geometry.RouteGeometryWay.GeometryGradientWayStyle;
import java.util.List;
public class RouteGeometryWayDrawer extends GeometryWayDrawer<RouteGeometryWayContext> {
private static final int BORDER_TYPE_ZOOM_THRESHOLD = MapTileLayer.DEFAULT_MAX_ZOOM + MapTileLayer.OVERZOOM_IN;
private final boolean drawBorder;
public RouteGeometryWayDrawer(RouteGeometryWayContext context, boolean drawBorder) {
super(context);
this.drawBorder = drawBorder;
}
@Override
protected void drawFullBorder(Canvas canvas, int zoom, List<Pair<Path, GeometryWayStyle<?>>> paths) {
if (drawBorder && zoom < BORDER_TYPE_ZOOM_THRESHOLD) {
Paint borderPaint = getContext().getAttrs().shadowPaint;
Path fullPath = new Path();
for (Pair<Path, GeometryWayStyle<?>> path : paths) {
if (path.second instanceof GeometryGradientWayStyle) {
fullPath.addPath(path.first);
}
}
canvas.drawPath(fullPath, borderPaint);
}
}
@Override
public void drawPath(Canvas canvas, Path path, GeometryWayStyle<?> s) {
if (s instanceof GeometryGradientWayStyle) {
GeometryGradientWayStyle style = (GeometryGradientWayStyle) s;
LinearGradient gradient = new LinearGradient(style.startXY.x, style.startXY.y, style.endXY.x, style.endXY.y,
style.startColor, style.endColor, Shader.TileMode.CLAMP);
getContext().getAttrs().customColorPaint.setShader(gradient);
}
super.drawPath(canvas, path, s);
}
@Override
protected void drawSegmentBorder(Canvas canvas, int zoom, Path path, GeometryWayStyle<?> style) {
if (drawBorder && zoom >= BORDER_TYPE_ZOOM_THRESHOLD) {
canvas.drawPath(path, getContext().getAttrs().shadowPaint);
}
}
}