diff options
| author | Bruno Perles | 2015-12-16 10:02:29 +0100 |
|---|---|---|
| committer | Bruno Perles | 2015-12-16 10:02:29 +0100 |
| commit | 013f4fa8fe9bb08f3ed1d15f905ca2a8437d6aa7 (patch) | |
| tree | 426b9c17167c10547da2222517cbd4433ae554fe /app | |
| parent | 2590606c5912a85b8cb1aaa40c57dab67d75e7f7 (diff) | |
| download | chouette-core-013f4fa8fe9bb08f3ed1d15f905ca2a8437d6aa7.tar.bz2 | |
Add route_sections for traces
Diffstat (limited to 'app')
29 files changed, 964 insertions, 13 deletions
diff --git a/app/assets/images/icons/edit-disabled.png b/app/assets/images/icons/edit-disabled.png Binary files differnew file mode 100644 index 000000000..aaefed65c --- /dev/null +++ b/app/assets/images/icons/edit-disabled.png diff --git a/app/assets/javascripts/route_section.js.coffee b/app/assets/javascripts/route_section.js.coffee new file mode 100644 index 000000000..f2e494c0c --- /dev/null +++ b/app/assets/javascripts/route_section.js.coffee @@ -0,0 +1,58 @@ +class @RouteSectionMap + @onSelectedFeature: (feature) -> + route_section_id = feature.data.id + + routeSectionOption = $("option[value=#{route_section_id}]") + routeSectionOption.parent().val route_section_id + + $('#map-selection').show() + $('#empty-map-selection').hide() + + selectionUrl = location.pathname.replace /edit$/, "selection" + $.ajax(url: selectionUrl, method: 'POST', data: { route_section_id: route_section_id }, dataType: 'html').done (data) -> + $('#map-selection div').replaceWith(data) + + @onUnselectedFeature: (feature) -> + $('#map-selection').hide() + $('#empty-map-selection').show() + +jQuery -> + if $("#map.route_section").length > 0 and user_geometry? + projWGS84 = new OpenLayers.Projection("EPSG:4326") + proj900913 = new OpenLayers.Projection("EPSG:900913") + wtk_format = new OpenLayers.Format.WKT() + + user_geometry.events.on({ + afterfeaturemodified: (event) -> + wgs84_geometry = event.feature.geometry.transform(proj900913, projWGS84) + wgs84_feature = new OpenLayers.Feature.Vector(wgs84_geometry) + ewtk = "SRID=4326;#{wtk_format.write(wgs84_feature)}" + + $('#route_section_editable_geometry').val(ewtk) + return + }) + + $('#new_route_sections_selector select').on 'change', -> + new_route_section_id = $(this).val() + + edit_link = $(this).closest("tr").find("a.edit-route-section") + + # Save edit link to play with it + unless edit_link.data("href-pattern")? + edit_link.data "href-pattern", edit_link.attr('href').replace(new RegExp("/route_sections/([0-9]+)/edit"), "/route_sections/:id/edit") + + if !!new_route_section_id + edit_link.removeClass "disabled" + edit_link.attr 'href', edit_link.data("href-pattern").replace(/:id/, new_route_section_id) + else + edit_link.addClass "disabled" + edit_link.attr 'href', '#' + + $('form.route_section').find('button[type="submit"]').on 'click', (e) -> + e.preventDefault(); + if typeof modify_feature != 'undefined' + modify_feature.deactivate() + $('form.route_section').submit() + return + + return diff --git a/app/assets/stylesheets/main/route_sections.css.scss b/app/assets/stylesheets/main/route_sections.css.scss new file mode 100644 index 000000000..cc1c85abc --- /dev/null +++ b/app/assets/stylesheets/main/route_sections.css.scss @@ -0,0 +1,68 @@ +#workspace.route_sections_selectors.edit { + td.route_section { + .input { + padding: 0; + margin: 0; + + select { + width: 100%; + } + } + } + + a.edit-route-section { + background: url(image-path('icons/edit.png')) no-repeat 0% 50%; + text-indent: -9999px; + display: inline-block; + width: 16px; + height: 16px; + + margin-right: 8px; + + &.disabled { + background-image: url(image-path('icons/edit-disabled.png')); + } + } + + #map-selection { + position: relative; + span { + display: inline-block; + } + .departure, .arrival { + width: 25%; + } + .actions { + width: 25%; + overflow: visible; + padding-left: 0; + position: absolute; + right: 0; + } + } +} + +#workspace.route_sections.index { + th.distance, th.points { + text-align: center; + } + + td.distance, td.points, td.actions { + text-align: center; + } +} + +#workspace.route_sections.edit { + .formtastic { + .input .label { + width: 40%; + } + .actions { + padding-left: 19%; + } + } + #map { + width: 600px; + heigth: 600px; + } +} diff --git a/app/assets/stylesheets/main/routes.scss b/app/assets/stylesheets/main/routes.scss index 7714c13a3..44703c7e5 100644 --- a/app/assets/stylesheets/main/routes.scss +++ b/app/assets/stylesheets/main/routes.scss @@ -3,7 +3,7 @@ // You can use Sass (SCSS) here: http://sass-lang.com/ #workspace.lines.show -{ +{ } #workspace.routes.edit, #workspace.routes.new, #workspace.routes.create, #workspace.routes.update @@ -11,13 +11,13 @@ #route_color{ width: 100px; color: white; font-weight: bold;} - + #stop_points .nested-fields { ol { margin-left: 25%; - .handle { margin-left: 5px;} + .handle { margin-left: 5px;} - .search_stop_area { + .search_stop_area { margin-bottom: 0px !important; } @@ -31,7 +31,7 @@ #stop_points .links { margin: 10px 0 15px 25%; - } + } } #workspace.routes.edit_boarding_alighting{ @@ -39,10 +39,18 @@ .stop_area{ padding-top: 7px; } - + } #workspace.routes.show -{ -} +{ +} +.large-map { + width: 100%; + height: 600px; + #map { + width: 100%; + height: 100%; + } +} diff --git a/app/assets/stylesheets/modules/icons.scss b/app/assets/stylesheets/modules/icons.scss index 001e511d1..3c42c3bd6 100644 --- a/app/assets/stylesheets/modules/icons.scss +++ b/app/assets/stylesheets/modules/icons.scss @@ -5,6 +5,11 @@ a:before { vertical-align: middle; } +a.view:before { // fa-eye + content: "\f06e"; + color: $brand-success; +} + a.add:before { // fa-plus-circle content: "\f055"; color: $brand-success; diff --git a/app/controllers/route_sections_controller.rb b/app/controllers/route_sections_controller.rb new file mode 100644 index 000000000..64cb8c65c --- /dev/null +++ b/app/controllers/route_sections_controller.rb @@ -0,0 +1,70 @@ +class RouteSectionsController < ChouetteController + + defaults :resource_class => Chouette::RouteSection + + respond_to :html + respond_to :kml, :only => :show + + belongs_to :referential + + before_action :save_return_to_path, only: [:edit, :create_to_edit] + before_action ->(controller) { build_breadcrumb controller.action_name } + + helper_method :search + + def index + index! + end + + def new + @stop_areas = referential.stop_areas.with_geometry.order :name + new! + end + + def show + @map = RouteSectionMap.new(resource).with_helpers(self) + show! + end + + def edit + @map = RouteSectionMap.new(resource, true).with_helpers(self) + edit! + end + + def update + update! { session.delete(:return_to) } + end + + def create + create! { session.delete(:return_to) } + end + + def create_to_edit + route_section = Chouette::RouteSection.create(route_section_params) + redirect_to edit_referential_route_section_path(referential, route_section) + end + + protected + + def save_return_to_path + session[:return_to] = params[:return_to] if params[:return_to] + end + + def collection + # if q = params[:q] + # @route_sections ||= Chouette::RouteSection.joins(:departure, :arrival).where(departure: {name: "#{q}"}).or.where(arrival: {name: "#{q}"}) + # end + @route_sections ||= search.collection.includes(:departure, :arrival).paginate page: params[:page] + end + + def search + @search ||= RouteSectionSearch.new(params[:route_section_search]) + end + + private + + def route_section_params + params.require(:route_section).permit(:departure_id, :arrival_id, :editable_geometry, :no_processing) + end + +end diff --git a/app/controllers/route_sections_selectors_controller.rb b/app/controllers/route_sections_selectors_controller.rb new file mode 100644 index 000000000..05a399ae8 --- /dev/null +++ b/app/controllers/route_sections_selectors_controller.rb @@ -0,0 +1,42 @@ +class RouteSectionsSelectorsController < ChouetteController + + # singleton option makes association_chain crazy + #defaults singleton: true + + respond_to :html, only: [ :edit, :update ] + respond_to :js, only: :section + + belongs_to :referential do + belongs_to :line, :parent_class => Chouette::Line do + belongs_to :route, :parent_class => Chouette::Route do + belongs_to :journey_pattern, parent_class: Chouette::JourneyPattern + end + end + end + + def edit + @map = RouteSectionSelectorMap.new(resource).with_helpers(self) + end + + def selection + parent + + @route_section = referential.route_sections.find params[:route_section_id].to_i + render partial: 'selection', format: 'js' + end + + private + + def resource + @route_sections_selector ||= RouteSectionsSelector.new parent + end + + def build_resource + @route_sections_selector ||= RouteSectionsSelector.new parent, *resource_params + end + + def route_section_selector_params + params.require(:route_section_selector).permit() + end + +end diff --git a/app/controllers/routes_controller.rb b/app/controllers/routes_controller.rb index 6dfaf3dd0..59c129867 100644 --- a/app/controllers/routes_controller.rb +++ b/app/controllers/routes_controller.rb @@ -75,7 +75,7 @@ class RoutesController < ChouetteController end private - + def route_params params.require(:route).permit( :direction_code, :wayback_code, :line_id, :objectid, :object_version, :creation_time, :creator_id, :name, :comment, :opposite_route_id, :published_name, :number, :direction, :wayback, { stop_points_attributes: [ :id, :_destroy, :position, :stop_area_id, :for_boarding, :for_alighting ] } ) end diff --git a/app/controllers/stop_areas_controller.rb b/app/controllers/stop_areas_controller.rb index 9321ac9ae..dbbadb80c 100644 --- a/app/controllers/stop_areas_controller.rb +++ b/app/controllers/stop_areas_controller.rb @@ -137,6 +137,6 @@ class StopAreasController < ChouetteController def stop_area_params params.require(:stop_area).permit( :routing_stop_ids, :routing_line_ids, :children_ids, :stop_area_type, :parent_id, :objectid, :object_version, :creation_time, :creator_id, :name, :comment, :area_type, :registration_number, :nearest_topic_name, :fare_code, :longitude, :latitude, :long_lat_type, :country_code, :street_name, :zip_code, :city_name, :mobility_restricted_suitability, :stairs_availability, :lift_availability, :int_user_needs, :coordinates, :url, :time_zone ) - end + end end diff --git a/app/helpers/breadcrumb_helper.rb b/app/helpers/breadcrumb_helper.rb index 52da8b887..69494321b 100644 --- a/app/helpers/breadcrumb_helper.rb +++ b/app/helpers/breadcrumb_helper.rb @@ -1,6 +1,7 @@ module BreadcrumbHelper - def build_breadcrumb(action) + def build_breadcrumb(action) + action = action.to_sym case resource_class.to_s when "Chouette::Network" network_breadcrumb action @@ -30,6 +31,8 @@ module BreadcrumbHelper connection_link_breadcrumb action when "Chouette::TimeTable" time_table_breadcrumb action + when "Chouette::RouteSection" + route_section_breadcrumb action when "Chouette::Timeband" timeband_breadcrumb action when "StopAreaCopy" @@ -106,6 +109,12 @@ module BreadcrumbHelper add_breadcrumb breadcrumb_label(@time_table), referential_time_table_path(@referential, @time_table),:title => breadcrumb_tooltip(@time_table) if action == :edit end + def route_section_breadcrumb(action) + referential_breadcrumb + add_breadcrumb Chouette::RouteSection.model_name.human.pluralize, referential_route_sections_path(@referential) + add_breadcrumb breadcrumb_label(resource), referential_route_section_path(@referential, resource),:title => breadcrumb_tooltip(resource) if action.in?([:show, :edit]) + end + def timeband_breadcrumb(action) referential_breadcrumb add_breadcrumb Chouette::Timeband.model_name.human(:count => 2), referential_timebands_path(@referential) unless action == :index diff --git a/app/helpers/route_section_selectors_helper.rb b/app/helpers/route_section_selectors_helper.rb new file mode 100644 index 000000000..1be30066b --- /dev/null +++ b/app/helpers/route_section_selectors_helper.rb @@ -0,0 +1,23 @@ +module RouteSectionSelectorsHelper + + def link_to_edit_route_section(route_section) + classes = [ 'edit-route-section' ] + link ='#' + + if route_section.present? + return_to = edit_referential_line_route_journey_pattern_route_sections_selector_path(@referential, @line, @route, @journey_pattern) + link = edit_referential_route_section_path(@referential, route_section, return_to: return_to) + else + classes << 'disabled' + end + + link_to "Edit", link, class: classes, title: t('route_sections_selectors.edit.route_section.edit') + end + + def link_to_create_route_section(departure, arrival) + return_to = edit_referential_line_route_journey_pattern_route_sections_selector_path(@referential, @line, @route, @journey_pattern) + link_to t('route_sections_selectors.edit.route_section.new'), + create_to_edit_referential_route_sections_path(@referential, route_section: {departure_id:departure.id, arrival_id: arrival.id}, return_to: return_to) + end + +end diff --git a/app/maps/design/route_section_selector_style_map.rb b/app/maps/design/route_section_selector_style_map.rb new file mode 100644 index 000000000..12f192ec8 --- /dev/null +++ b/app/maps/design/route_section_selector_style_map.rb @@ -0,0 +1,73 @@ +class Design::RouteSectionSelectorStyleMap < Design::GenericStyleMap + attr_accessor :style + + def initialize(helpers, options = {}) + @helpers = helpers + @style = options[:style].present? ? default_style.merge(options[:style]) : default_style + end + + def select_style + { + fillColor: "blue", + graphicName: "square", + rotation: 90, + strokeColor: "#00dd00", + pointRadius: 15, + graphicZIndex: 100 + } + end + + def highlight_style + { + fillColor: "orange", + graphicName: "square", + rotation: 90, + strokeColor: "#dd0000", + pointRadius: 15, + graphicZIndex: 200 + } + end + + def default_style + { + label: "${label}", + fontColor: "black", + fontSize: "11px", + fontWeight: "bold", + labelAlign: "ct", + labelXOffset: 0, + labelYOffset: -15, + strokeColor: "#0000dd", + strokeOpacity: 1, + strokeWidth: 3, + strokeLineCap: "round", + strokeDashstyle: "solid", + externalGraphic: @helpers.assets_path_patch( "map/${positionType}.png"), + graphicWidth: 12, + graphicHeight: 12, + graphicOpacity: 1, + graphicXOffset: -6, + graphicYOffset: -6, + display: true, + fillColor: "red", + graphicName: "square", + rotation: 90, + graphicZIndex: 10 + } + end + + def context + { + label: :" function(feature) {if(feature.layer.map.getZoom() > 13) { return feature.attributes.name;} else {return '';}} ", + positionType: :" function(feature) { if (feature.attributes.iconCode != undefined) {return feature.attributes.iconCode;} else { return '';} } " + } + end + + def style_map + OpenLayers::StyleMap.new( + default: OpenLayers::Style.new(style, { context: context}), + select: OpenLayers::Style.new(style.merge(select_style), { context: context }), + highlight: OpenLayers::Style.new(style.merge( highlight_style), { context: context })) + end + +end diff --git a/app/maps/design/route_section_style_map.rb b/app/maps/design/route_section_style_map.rb new file mode 100644 index 000000000..2c94bb3a0 --- /dev/null +++ b/app/maps/design/route_section_style_map.rb @@ -0,0 +1,66 @@ +class Design::RouteSectionStyleMap < Design::GenericStyleMap + attr_accessor :style + + def initialize(helpers, options = {}) + @helpers= helpers + @style = options[:style].present? ? default_style.merge(options[:style]) : default_style + end + + def select_style + { + fillColor:"blue", + graphicName:"circle", + rotation:90, + :strokeColor => "red", + pointerEvents: "visiblePainted" + } + end + + def highlight_style + { + fillColor:"lightblue", + graphicName:"circle", + rotation:90, + :strokeColor => "#dd0000" + } + end + + def default_style + + { + :fontColor => "black", + :fontSize => "11px", + :fontWeight => "bold", + :labelAlign => "cm", + :labelXOffset => 0, + :labelYOffset => -15, + :strokeColor => "green", + :strokeOpacity => 1, + :strokeWidth => 2, + :strokeLineCap => "round", + :strokeLineJoin => "round", + :strokeDashstyle => "solid", + :lineCap => "round", + :lineJoin => "round", + :dashstyle => "solid", + :graphicWidth => 12, + :graphicHeight => 12, + :graphicOpacity => 1, + :graphicXOffset => -6, + :graphicYOffset => -6, + :display => true, + fillOpacity: 0.5, + fillColor: "lightblue", + graphicName: "circle", + pointRadius: 6, + pointerEvents: "visiblePainted", + rotation: 90 + } + + end + + def style_map + OpenLayers::StyleMap.new(:default => OpenLayers::Style.new(style), :select => OpenLayers::Style.new(style.merge( select_style)), :highlight => OpenLayers::Style.new(style.merge( highlight_style))) + end + +end diff --git a/app/maps/route_section_map.rb b/app/maps/route_section_map.rb new file mode 100644 index 000000000..cfa40d481 --- /dev/null +++ b/app/maps/route_section_map.rb @@ -0,0 +1,67 @@ +class RouteSectionMap < ApplicationMap + + attr_reader :route_section + + attr_accessor :editable + alias_method :editable?, :editable + + def initialize(route_section, editable = false) + @route_section = route_section + @editable = editable + end + + def customize_map(map, page) + # layers order seems to matter for ModifyFeature control + route_section.stop_areas.each do |stop_area| + layer = "stop_area_#{stop_area.id}".to_sym + page.assign layer.to_s, kml_layer(stop_area, :styleMap => Design::StopAreasStyleMap.new(helpers).style_map) + page << map.add_layer(layer) + page << map.add_control( hover_control_display_name(layer) ) + end + + geometry_options = {}.tap do |options| + options[:mode] = :editable if editable? + end + geometry_uneditable_kml_layer = kml_layer(route_section, :styleMap => Design::RouteSectionSelectorStyleMap.new(helpers).style_map) + page << map.add_layer(geometry_uneditable_kml_layer) + + if route_section.input_geometry + geometry_editable_layer = kml_layer(route_section, geometry_options, {}) # , :styleMap => Design::LineStyleMap.new(style: nil).style_map) + else + points = route_section.stop_areas.map{|point| OpenLayers::Geometry::Point.new(point.longitude, point.latitude).transform("EPSG:4326", "EPSG:900913")} + geometry_editable_layer = OpenLayers::Layer::Vector.new("user_geometry", {:projection => projection("EPSG:4326"), :styleMap => Design::RouteSectionStyleMap.new(helpers).style_map}) + #geometry_editable_layer = OpenLayers::Layer::Vector.new("user_geometry", {:projection => projection("EPSG:4326")}) + geometry_editable_features = OpenLayers::Feature::Vector.new(OpenLayers::Geometry::LineString.new(points)) + page.assign :geometry_editable_features, geometry_editable_features + end + + if editable + page.assign :user_geometry, geometry_editable_layer + + page << "user_geometry.addFeatures([geometry_editable_features])" if geometry_editable_features + + page << map.add_layer(:user_geometry) + + page.assign :modify_feature, OpenLayers::Control::ModifyFeature.new(:user_geometry, autoActivate: true) + page << map.add_control( :modify_feature ) + else + page << map.add_layer(geometry_editable_layer) + end + + page << map.zoom_to_extent(bounds.to_google.to_openlayers) if bounds + end + + def bounds + @bounds ||= + if route_section.geometry.present? + route_section.geometry.bounds + elsif route_section.stop_areas.present? + GeoRuby::SimpleFeatures::Point.bounds route_section.stop_areas.collect(&:geometry) + end + end + + def ready? + bounds.present? + end + +end diff --git a/app/maps/route_section_selector_map.rb b/app/maps/route_section_selector_map.rb new file mode 100644 index 000000000..425a6750a --- /dev/null +++ b/app/maps/route_section_selector_map.rb @@ -0,0 +1,118 @@ +class RouteSectionSelectorMap < ApplicationMap + + attr_reader :route_section_selector, :style + + def initialize(route_section_selector, style = nil) + @route_section_selector = route_section_selector + @style = nil + end + + def customize_map(map, page) + layer = kml_layer([route_section_selector.itinerary.referential, route_section_selector.itinerary.route.line, route_section_selector.itinerary.route, route_section_selector.itinerary], + { rendererOptions: { zIndexing: true }, styleMap: Design::JourneyPatternStyleMap.new(helpers).style_map }) + page.assign 'journeyPatternLayer', layer + + page << map.add_layer(:journeyPatternLayer) + page << map.add_control(hover_control_display_name(:journeyPatternLayer)) + + route_section_geometry = OpenLayers::Layer::Vector.new('Route Section Geometry', + { projection: projection('EPSG:900913'), + rendererOptions: { zIndexing: true }, + styleMap: Design::RouteSectionSelectorStyleMap.new(helpers).style_map}) + + route_section_layer_points = [] + route_section_selector.sections.reject{|s| s.candidates.length==0}.each do |section| + section.candidates.each do |candidate| + geometry = candidate.processed_geometry + route_section_layer_points << ["#{candidate.id}"] + clean_route_section_line(geometry) + end + end + + page.assign :route_section_layer_points, route_section_layer_points + page << <<EOF + var route_section_layers; + route_section_layers = route_section_layer_points.map(function(elt, index) { + var route_section_id = elt[0]; + elt.splice(0, 1); + var points = elt.map( function(e,i) { + return OpenLayers.Projection.transform(new OpenLayers.Geometry.Point(e[0], e[1]), "EPSG:4326", "EPSG:900913" ); + }) + return new OpenLayers.Feature.Vector(new OpenLayers.Geometry.LineString(points), {id: route_section_id, name: ""}); + }) +EOF + page.assign :route_section_geometry, route_section_geometry + page << 'route_section_geometry.addFeatures(route_section_layers)' + page << map.add_layer(:route_section_geometry) + + page << <<EOF + function selected(feature) { + RouteSectionMap.onSelectedFeature(feature); + } + function unselected(feature) { + RouteSectionMap.onUnselectedFeature(feature); + } + + highlightControl = new OpenLayers.Control.SelectFeature([route_section_geometry], + { + clickout: true, + toggle: false, + multiple:false, + hover:true, + highlightOnly:true, + eventListeners:{ + featurehighlighted: function (event) { + event.feature.layer.drawFeature( + event.feature, + 'highlight' + ); + }, + featureunhighlighted: function (event) { + event.feature.layer.drawFeature( + event.feature, + 'default' + ); + } + } + } + ); + selectControl = new OpenLayers.Control.SelectFeature([route_section_geometry], + { + onSelect:selected, + onUnselect:unselected + } + ); + map.addControl(highlightControl); + map.addControl(selectControl); + highlightControl.activate(); + selectControl.activate(); +EOF + + #page.assign :select_feature, OpenLayers::Control::SelectFeature.new(:route_section_geometry, {onSelect: selected, onUnselect: unselected}) + #page << map.add_control( :select_feature ) + + + page << map.zoom_to_extent(bounds.to_google.to_openlayers) if bounds + + end + + def clean_route_section_line(line) + point_array = line.to_s.scan(/\d+[.]\d+\s\d+[.]\d+/) + proj = OpenLayers::Projection.new('proj') + + point_array.map do |point| + point = point.scan(/\d+[.]\d+/) + lat = point[0].to_f + lng = point[1].to_f + [lat, lng] + end + + end + + def ready? + bounds.present? + end + + def bounds + @bounds ||= GeoRuby::SimpleFeatures::Point.bounds(route_section_selector.itinerary.route.stop_areas.collect(&:geometry).compact) + end +end diff --git a/app/models/referential.rb b/app/models/referential.rb index e32c956fb..d537246e5 100644 --- a/app/models/referential.rb +++ b/app/models/referential.rb @@ -91,6 +91,10 @@ class Referential < ActiveRecord::Base Chouette::VehicleJourney.all end + def route_sections + Chouette::RouteSection.all + end + after_initialize :define_default_attributes def define_default_attributes diff --git a/app/models/route_section_search.rb b/app/models/route_section_search.rb new file mode 100644 index 000000000..a03eb894d --- /dev/null +++ b/app/models/route_section_search.rb @@ -0,0 +1,34 @@ +class RouteSectionSearch + include ActiveModel::Conversion + extend ActiveModel::Naming + + attr_accessor :departure_name, :arrival_name, :line_id + attr_accessor :scope + + def scope + scope ||= Chouette::RouteSection + end + + def initialize(attributes = {}) + attributes.each { |k,v| send "#{k}=", v } if attributes + end + + def collection() + Rails.logger.debug "Search RouteSections with #{inspect}" + collection = scope + + [:departure, :arrival].each do |endpoint| + endpoint_name = send "#{endpoint}_name" + collection = collection.by_endpoint_name(endpoint, endpoint_name) if endpoint_name.present? + end + + collection = collection.by_line_id(line_id) if line_id.present? + + collection + end + + def persisted? + false + end + +end diff --git a/app/models/route_sections_selector.rb b/app/models/route_sections_selector.rb new file mode 100644 index 000000000..ba8a2cc9a --- /dev/null +++ b/app/models/route_sections_selector.rb @@ -0,0 +1,99 @@ +class RouteSectionsSelector + extend ActiveModel::Naming + include ActiveModel::Conversion + + include ActiveModel::Validations + + attr_reader :itinerary + + def initialize(route_or_journey_pattern, attributes = {}) + @itinerary = route_or_journey_pattern + + self.attributes = attributes + end + + def attributes=(attributes) + attributes.each { |k,v| send "#{k}=", v } + end + + def update_attributes(attributes) + self.attributes = attributes + save + end + + # def persisted? + # false + # end + + delegate :stop_points, to: :itinerary + + def route_sections + @route_sections ||= itinerary.route_sections.to_a + end + + def sections + @sections ||= create_sections + end + + def create_sections + [].tap do |sections| + stop_points.each_cons(2).each_with_index do |(departure, arrival), index| + sections << Section.new(departure.stop_area, arrival.stop_area, route_sections[index]) + end + end + end + + def sections_attributes=(attributes) + # Process the attributes hash + attributes.each do |index, section_attributes| + sections[index.to_i].attributes = section_attributes + end + end + + def save + itinerary.update_attribute :route_section_ids, sections.map(&:route_section_id) + end + + class Section + extend ActiveModel::Translation + + attr_accessor :departure, :arrival, :route_section_id + + def initialize(departure, arrival, route_section = nil) + @departure, @arrival = departure, arrival + + self.route_section = route_section + end + + def route_section=(route_section) + @route_section = route_section + @route_section_id = route_section.respond_to?(:id) ? route_section.id : nil + end + + def route_section + @route_section ||= candidates.find_by id: route_section_id + end + + def persisted? + false + end + + def candidates + @candidates ||= Chouette::RouteSection.where(departure: departure, arrival: arrival) + end + + def create_candidate + Chouette::RouteSection.create(departure: departure, arrival: arrival) + end + + def attributes=(attributes) + attributes.each { |k,v| send "#{k}=", v } + end + + def valid? + route_section.present? + end + + end + +end diff --git a/app/views/journey_patterns/show.html.erb b/app/views/journey_patterns/show.html.erb index dc9e852fa..3a2b18ea1 100644 --- a/app/views/journey_patterns/show.html.erb +++ b/app/views/journey_patterns/show.html.erb @@ -34,9 +34,9 @@ <ul class="actions"> <li><%= link_to t('journey_patterns.actions.new'), new_referential_line_route_journey_pattern_path(@referential, @line, @route), :class => "add" %></li> <li><%= link_to t('journey_patterns.actions.edit'), edit_referential_line_route_journey_pattern_path(@referential, @line, @route, @journey_pattern), :class => "edit" %></li> - <li><%= link_to t('journey_patterns.actions.destroy'), referential_line_route_journey_pattern_path(@referential, @line, @route, @journey_pattern), :method => :delete, :data => {:confirm => t('journey_patterns.actions.destroy_confirm')}, :class => "remove" %></li> + <li><%= link_to t('journey_patterns.actions.destroy'), referential_line_route_journey_pattern_path(@referential, @line, @route, @journey_pattern), :method => :delete, :data => {:confirm => t('journey_patterns.actions.destroy_confirm')}, :class => "remove" %></li> + <li><%= link_to t('journey_patterns.actions.edit_route_sections'), edit_referential_line_route_journey_pattern_route_sections_selector_path(@referential, @line, @route, @journey_pattern), class: 'edit' %></li> <li><%= link_to t('journey_patterns.journey_pattern.vehicle_journey_at_stops'), referential_line_route_vehicle_journeys_path(@referential, @line, @route, :q => {:journey_pattern_id_eq => @journey_pattern.id}), :class => "clock" %></li> </ul> <%= creation_tag(@journey_pattern) %> <% end %> - diff --git a/app/views/referentials/_counts.html.erb b/app/views/referentials/_counts.html.erb index 3ade4e945..20078429b 100644 --- a/app/views/referentials/_counts.html.erb +++ b/app/views/referentials/_counts.html.erb @@ -32,6 +32,10 @@ <%= Referential.human_attribute_name("connection_links") %> </li> <li class="list-group-item"> + <span class="badge"><%= @referential.route_sections.size %></span> + <%= link_to Referential.human_attribute_name("route_sections"), referential_route_sections_path(@referential) %> + </li> + <li class="list-group-item"> <span class="badge"><%= @referential.stop_areas.size %></span> <%= Referential.human_attribute_name("stop_areas") %> </li> diff --git a/app/views/route_sections/_form.html.erb b/app/views/route_sections/_form.html.erb new file mode 100644 index 000000000..28a1dd47a --- /dev/null +++ b/app/views/route_sections/_form.html.erb @@ -0,0 +1,24 @@ +<%= semantic_form_for [@referential, @route_section] do |form| %> + <%= form.inputs do %> + <% if @route_section.new_record? %> + <%= form.input :departure, as: :select, collection: @stop_areas, include_blank: false %> + <%= form.input :arrival, as: :select, collection: @stop_areas, include_blank: false %> + <% else %> + <li class="input optional"> + <label class="label"><%= @route_section.human_attribute_name("departure") %></label> + <%= @route_section.departure.name %> + </li> + <li class="input optional"> + <label class="label"><%= @route_section.human_attribute_name("arrival") %></label> + <%= @route_section.arrival.name %> + </li> + <%= form.input :editable_geometry, as: :hidden %> + <% end %> + + <%= form.input :no_processing, label: t('.no_processing'), as: :select, collection: { t('.standard_processor') => false, t('.no_processor') => true }, include_blank: false %> + <% end %> + <%= form.actions do %> + <%= form.action :submit, :as => :button, label: t('.submit') %> + <%= form.action :cancel, :as => :link %> + <% end %> +<% end %> diff --git a/app/views/route_sections/edit.html.erb b/app/views/route_sections/edit.html.erb new file mode 100644 index 000000000..80b7bde4a --- /dev/null +++ b/app/views/route_sections/edit.html.erb @@ -0,0 +1,4 @@ +<%= title_tag t('route_sections.edit.title') %> + +<%= @map.to_html %> +<%= render "form" %> diff --git a/app/views/route_sections/index.html.erb b/app/views/route_sections/index.html.erb new file mode 100644 index 000000000..5d7bef989 --- /dev/null +++ b/app/views/route_sections/index.html.erb @@ -0,0 +1,56 @@ +<%= title_tag t('.title') %> + +<%= form_for search, url: referential_route_sections_path(@referential), method: :get, class: "form-inline" do |form| %> + <div class="panel panel-default"> + <div class="panel-heading"> + <div class="col-md-6"> + <%= form.text_field :departure_name, class: 'form-control', placeholder: Chouette::RouteSection.human_attribute_name(:departure) %> + </div> + + <div class="input-group col-md-6"> + <%= form.text_field :arrival_name, class: 'form-control', placeholder: Chouette::RouteSection.human_attribute_name(:arrival) %> + <div class="input-group-btn"> + <button class="btn btn-default" type="submit"><i class="fa fa-search"></i></button> + </div> + </div> + </div> + + <div class="panel-body"> + <div class="col-md-4"> + <%= form.select :line_id, @referential.lines.order(:name).pluck(:name, :id), { include_blank: t('.all_lines')}, class: 'form-control' %> + </div> + </div> + </div> +<% end %> + +<table class="table table-hover table-striped"> + <thead> + <tr> + <th><%= Chouette::RouteSection.human_attribute_name :departure %></th> + <th><%= Chouette::RouteSection.human_attribute_name :arrival %></th> + <th class="distance"><%= Chouette::RouteSection.human_attribute_name :distance %></th> + <th class="points"><%= Chouette::RouteSection.human_attribute_name :points %></th> + <th class="actions"></th> + </tr> + </thead> + <tbody> + <% @route_sections.each do |route_section| %> + <tr> + <td><%= route_section.departure.name %> <%= link_to content_tag(:i, "", class: "fa fa-external-link"), referential_stop_area_path(@referential, route_section.departure) %></td> + <td><%= route_section.arrival.name %> <%= link_to content_tag(:i, "", class: "fa fa-external-link"), referential_stop_area_path(@referential, route_section.arrival) %></td> + <td class="distance"><%= route_section.distance.to_i if route_section.distance %></td> + <td class="points"><%= route_section.via_count if route_section.via_count > 0 %></td> + <td class="actions"> + <%= link_to t('.actions.show'), referential_route_section_path(@referential, route_section), class: "view" %> + <%= link_to t('.actions.edit'), edit_referential_route_section_path(@referential, route_section), class: "edit" %> + <%= link_to t('.actions.destroy'), referential_route_section_path(@referential, route_section), method: :delete, data: { confirm: t('route_sections.actions.destroy_confirm') }, class: "remove" %> + </td> + </tr> + <% end %> + </tbody> + +</table> + +<div class="pagination"> + <%= will_paginate @route_sections, :container => false, renderer: RemoteBootstrapPaginationLinkRenderer %> +</div> diff --git a/app/views/route_sections/new.html.erb b/app/views/route_sections/new.html.erb new file mode 100644 index 000000000..1ff3c74d0 --- /dev/null +++ b/app/views/route_sections/new.html.erb @@ -0,0 +1,3 @@ +<%= title_tag t('route_sections.new.title') %> + +<%= render "form" %> diff --git a/app/views/route_sections/show.html.erb b/app/views/route_sections/show.html.erb new file mode 100644 index 000000000..61c7c8149 --- /dev/null +++ b/app/views/route_sections/show.html.erb @@ -0,0 +1,28 @@ +<%= title_tag t('.title') %> + +<div class="route_section"> + <%= @map.to_html %> + + <div class="summary"> + <p> + <label><%= @route_section.human_attribute_name("departure") %>: </label> + <%= @route_section.departure.name %> + </p> + <p> + <label><%= @route_section.human_attribute_name("arrival") %>: </label> + <%= @route_section.arrival.name %> + </p> + <p> + <label><%= @route_section.human_attribute_name("distance") %>: </label> + <%= "#{@route_section.distance.round}m" if @route_section.distance %> + </p> + </div> +</div> + +<% content_for :sidebar do %> +<ul class="actions"> + <li><%= link_to t('route_sections.actions.edit'), edit_referential_route_section_path(@referential, @route_section), :class => "edit" %></li> + <li><%= link_to t('route_sections.actions.destroy'), referential_route_section_path(@referential, @route_section), :method => :delete, :data => {:confirm => t('route_sections.actions.destroy_confirm')}, :class => "remove" %></li> +</ul> + <%= creation_tag(@route_section) %> +<% end %> diff --git a/app/views/route_sections/show.kml.erb b/app/views/route_sections/show.kml.erb new file mode 100644 index 000000000..0d41f0fdb --- /dev/null +++ b/app/views/route_sections/show.kml.erb @@ -0,0 +1,8 @@ +<?xml version="1.0" encoding="UTF-8"?> +<kml xmlns="http://www.opengis.net/kml/2.2"> + <Document> + <Placemark id="route-section-<%= @route_section.id %>"> + <%= @route_section.geometry(params[:mode]).kml_representation.html_safe %> + </Placemark> + </Document> +</kml> diff --git a/app/views/route_sections_selectors/_selection.js.erb b/app/views/route_sections_selectors/_selection.js.erb new file mode 100644 index 000000000..70d03cf75 --- /dev/null +++ b/app/views/route_sections_selectors/_selection.js.erb @@ -0,0 +1,28 @@ +<div> + <span class="departure"> + <label><%= @route_section.human_attribute_name("departure") %>:</label> + <%= link_to @route_section.departure.name, referential_stop_area_path(@referential, @route_section.departure) %> + </span> + + <span class="arrival"> + <label><%= @route_section.human_attribute_name("arrival") %>:</label> + <%= link_to @route_section.arrival.name, referential_stop_area_path(@referential, @route_section.arrival) %> + </span> + + <span class="distance"> + <label><%= @route_section.human_attribute_name("distance") %>:</label> + <%= "#{@route_section.distance.to_i} m" if @route_section.distance > 0 %> + </span> + + <% if @route_section.via_count > 0 %> + <span class="via_count"> + <label><%= @route_section.human_attribute_name("via_count") %>:</label> + <%= @route_section.via_count if @route_section.via_count > 0 %> + </span> + <% end %> + + <span class="actions"> + <%= link_to_edit_route_section @route_section %> + <%= link_to_create_route_section @route_section.departure, @route_section.arrival %> + </span> +</div> diff --git a/app/views/route_sections_selectors/edit.html.erb b/app/views/route_sections_selectors/edit.html.erb new file mode 100644 index 000000000..0cd58d9c1 --- /dev/null +++ b/app/views/route_sections_selectors/edit.html.erb @@ -0,0 +1,48 @@ +<%= title_tag t('.title', journey_pattern: journey_name(@journey_pattern)) %> + +<div class = "large-map"> + <%= @map.to_html %> +</div> + +<%= semantic_form_for resource, url: referential_line_route_journey_pattern_route_sections_selector_path(@referential, @line, @route, @journey_pattern), method: :put do |form| %> + +<h3><%= t('.selection.title') %></h3> + +<div id="map-selection" style="display: none"> + <div> + </div> +</div> + +<div id="empty-map-selection"> + <%= t('.no_selection') %> +</div> + +<h3><%= t('.form.title') %></h3> + +<table class="table table-hover table-striped"> + <tbody> + <%= form.fields_for :sections do |section_form| %> + <tr> + <td><%= link_to section_form.object.departure.name, referential_stop_area_path(@referential, section_form.object.departure) %></td> + <td class="route_section"> + <% if section_form.object.candidates.present? %> + <%= section_form.input :route_section_id, as: :select, label: false, collection: section_form.object.candidates, include_blank: t('.no_selection') %> + <% else %> + <%= t('.no_candidate') %> + <% end %> + </td> + <td class="action"> + <%= link_to_edit_route_section section_form.object.route_section %> + <%= link_to_create_route_section section_form.object.departure, section_form.object.arrival %> + </td> + </tr> + <% end %> + </tbody> +</table> + +<%= form.actions do %> + <%= form.action :submit, as: :button, label: t('.submit') %> + <%= form.action :cancel, as: :link, url: referential_line_route_journey_pattern_path(@referential, @line, @route, @journey_pattern) %> +<% end %> + +<% end %> diff --git a/app/views/shared/_header.erb b/app/views/shared/_header.erb index 1e8a63fba..9d53d807f 100644 --- a/app/views/shared/_header.erb +++ b/app/views/shared/_header.erb @@ -65,6 +65,10 @@ <span class="badge pull-right"><%= @referential.time_tables.size %></span><%= Referential.human_attribute_name("time_tables") %> <% end %> </li> + <li><%= link_to referential_route_sections_path(@referential) do %> + <span class="badge pull-right"><%= @referential.route_sections.size %></span><%= Referential.human_attribute_name("route_sections") %> + <% end %> + </li> <li><%= link_to referential_timebands_path(@referential) do %> <span class="badge pull-right"><%= @referential.timebands.size %></span><%= Referential.human_attribute_name("timebands") %> <% end %> |
